Advertisement
janac

Conversion from WAV to MP3

Sep 12th, 2021 (edited)
129
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 8.83 KB | None | 0 0
  1. // Conversion of WAV file to MP3 using LAME.
  2.  
  3. #include <iostream>
  4. #include <fstream>
  5. #include <string>
  6. #include <vector>
  7. #include "lame.h"
  8.  
  9. int main()
  10. {
  11.     std::streampos size; // stream pointer
  12.     char* memblock; // memory to store file bytes
  13.     // Open with pointer at end of file
  14.     std::ifstream file("C:\\jana\\csound\\projects\\building_csd_classes_output_files\\test.wav", std::ios::in | std::ios::binary | std::ios::ate);
  15.     // for LAME
  16.     std::vector<short int> data_left_channel;
  17.     std::vector<short int> data_right_channel;
  18.     short int* left_ptr = NULL;
  19.     short int* right_ptr = NULL;
  20.     int samples_per_channel = 0;
  21.     int buffer_size = 0;
  22.     std::vector<unsigned char>  mp3_frame_vector;
  23.     unsigned char* mp3_frames_ptr = NULL;
  24.     if (file.is_open())
  25.     {
  26.         size = file.tellg(); // pointer at end is the number of bytes in the file
  27.         std::cout << "This file is " << size << " bytes." << std::endl;
  28.         memblock = new char[size];
  29.         file.seekg(0);
  30.         file.read(memblock, size);
  31.         std::cout << "File container format: " << memblock[0] << memblock[1] << memblock[2] << memblock[3] << std::endl;
  32.         int read_int = int((unsigned char)(memblock[7]) << 24 | // little endian
  33.             (unsigned char)(memblock[6]) << 16 |
  34.             (unsigned char)(memblock[5]) << 8 |
  35.             (unsigned char)(memblock[4]));
  36.         std::cout << "Number of bytes remaining in the file: " << read_int << std::endl;
  37.         std::cout << "Data format: " << memblock[8] << memblock[9] << memblock[10] << memblock[11] << std::endl;
  38.         std::cout << "Format tag: " << memblock[12] << memblock[13] << memblock[14] << memblock[15] << std::endl; // last should be a pad byte.
  39.         read_int = int((unsigned char)(memblock[19]) << 24 | // little endian
  40.             (unsigned char)(memblock[18]) << 16 |
  41.             (unsigned char)(memblock[17]) << 8 |
  42.             (unsigned char)(memblock[16]));
  43.         std::cout << "The format information is " << read_int << " bytes." << std::endl;
  44.         read_int = int((unsigned char)(memblock[21]) << 8 |
  45.             (unsigned char)(memblock[20]));
  46.         if (read_int == 1) std::cout << "Audio format is PCM." << std::endl; else { std::cout << "Throw unsupported format exception here: 'The data format doesn't appear to be PCM. This is the only supported format.'"; }
  47.         read_int = int((unsigned char)(memblock[23]) << 8 |
  48.             (unsigned char)(memblock[22]));
  49.         std::string mono_or_stereo;
  50.         if (read_int == 1) mono_or_stereo = "Mono";
  51.         else if (read_int == 2) mono_or_stereo = "Stereo";
  52.         else { std::cout << "Throw unsupported number of channels exception here: 'This audio file doesn't appear to be mono or stereo. These are the only supported types of audio file.'"; }
  53.         std::cout << "Mono or stereo: " << mono_or_stereo << "." << std::endl;
  54.         read_int = int((unsigned char)(memblock[27]) << 24 | // little endian
  55.             (unsigned char)(memblock[26]) << 16 |
  56.             (unsigned char)(memblock[25]) << 8 |
  57.             (unsigned char)(memblock[24]));
  58.         std::cout << "Sample rate: " << read_int << " Hz." << std::endl;
  59.         read_int = int((unsigned char)(memblock[31]) << 24 | // little endian
  60.             (unsigned char)(memblock[30]) << 16 |
  61.             (unsigned char)(memblock[29]) << 8 |
  62.             (unsigned char)(memblock[28]));
  63.         std::cout << "Byte rate: " << read_int << " bytes/sec." << std::endl;
  64.         read_int = int((unsigned char)(memblock[33]) << 8 |
  65.             (unsigned char)(memblock[32]));
  66.         std::cout << "Block align: " << read_int << "." << std::endl;
  67.         read_int = int((unsigned char)(memblock[35]) << 8 |
  68.             (unsigned char)(memblock[34]));
  69.         std::cout << "Bits per sample: " << read_int << "." << std::endl;
  70.         std::cout << "Data tag: " << memblock[36] << memblock[37] << memblock[38] << memblock[39] << std::endl;
  71.         read_int = int((unsigned char)(memblock[43]) << 24 | // little endian
  72.             (unsigned char)(memblock[42]) << 16 |
  73.             (unsigned char)(memblock[41]) << 8 |
  74.             (unsigned char)(memblock[40]));
  75.         std::cout << "Size of audio data: " << read_int << " bytes." << std::endl;
  76.         // For LAME, I need:
  77.         // the left and right channel data in separate short int c arrays
  78.         // int number of samples per channel
  79.         // char* buffer, suggested size in bytes = 1.25 * num_samples + 7200
  80.         // int size of buffer
  81.         file.seekg(44); // beginning of the audio data
  82.         int ctr = 44;
  83.         std::vector<char> sample_left_channel; // a 2-byte sample
  84.         short int current_sample = 0;
  85.         while (file.tellg() < size) // Get the audio data for the left channel.
  86.         {
  87.             sample_left_channel.clear();
  88.             sample_left_channel.push_back(file.get());
  89.             file.seekg(++ctr);
  90.             sample_left_channel.push_back(file.get());
  91.             ctr += 3;
  92.             file.seekg(ctr);
  93.             current_sample = short int((unsigned char)(sample_left_channel[1]) << 8 | // little endian
  94.                 (unsigned char)(sample_left_channel[0]));
  95.             data_left_channel.push_back(current_sample);
  96.         }
  97.        
  98.         file.seekg(46);
  99.         ctr = 46;
  100.         std::vector<char> sample_right_channel; // a 2-byte sample
  101.         while (file.tellg() < size) // Get the audio data for the right channel.
  102.         {
  103.             sample_right_channel.clear();
  104.             sample_right_channel.push_back(file.get());
  105.             file.seekg(++ctr);
  106.             sample_right_channel.push_back(file.get());
  107.             ctr += 3;
  108.             file.seekg(ctr);
  109.             current_sample = short int((unsigned char)(sample_right_channel[1]) << 8 | // little endian
  110.                 (unsigned char)(sample_right_channel[0]));
  111.             data_right_channel.push_back(current_sample);
  112.         }
  113.        
  114.         file.close();
  115.         delete[] memblock;
  116.  
  117.         // arguments for lame_encode_buffer()
  118.         left_ptr = data_left_channel.data();
  119.         right_ptr = data_right_channel.data();
  120.         samples_per_channel = data_right_channel.size();
  121.         buffer_size = (1.25 * samples_per_channel) + 7200; // Suggested safe buffer size, from API.
  122.         mp3_frame_vector.resize(buffer_size); // Create a vector the right size so I can get the array inside.
  123.         mp3_frames_ptr = mp3_frame_vector.data();
  124.  
  125.         std::cout << std::endl << "Arguments done." << std::endl;
  126.     }
  127.     else std::cout << "File opening failed." << std::endl;
  128.  
  129.     // Initialize LAME.
  130.     lame_global_flags* gfp;
  131.     gfp = lame_init();
  132.     lame_set_bWriteVbrTag(gfp, 0); // because I'm not writing the Xing VBR/INFO tag to the mp3 file (see after flush below)
  133.     int init_result = lame_init_params(gfp);
  134.     if (init_result >= 0)
  135.         std::cout << "LAME initialization ok." << std::endl;
  136.  
  137.     // Encode the PCM data.
  138.     int result = lame_encode_buffer(gfp, // global pointer
  139.         left_ptr, right_ptr, // arrays of short int for left and right channels
  140.         samples_per_channel, mp3_frames_ptr, buffer_size); // samples/channel, the encoded mp3 frames, size of mp3 buffer
  141.     if (result >= 0) std::cout << "LAME output " << result << " bytes to mp3 buffer." << std::endl;
  142.     else std::cout << "Something went wrong during encoding. LAME error code: " << result << std::endl;
  143.  
  144.     // The size of the buffer was calculated using a very safe calculation, mostly likely much more space than was needed.
  145.     // Now removing any empty space at the end.
  146.     std::cout << "Removing extra space at the end of the buffer ..." << std::endl;
  147.     std::vector<unsigned char> smaller_buffer(result);
  148.     unsigned char* mp3_frames_no_extra_space_ptr = smaller_buffer.data();
  149.     for (int x = 0; x < result; ++x)
  150.     {
  151.         *(mp3_frames_no_extra_space_ptr + x) = *(mp3_frames_ptr + x); // Copy only mp3 frames to new buffer, omitting extra space.
  152.     }
  153.     std::cout << "Smaller buffer done." << std::endl;
  154.  
  155.     // Write the encoded data to disk.
  156.     std::cout << "Writing mp3 data to disk ..." << std::endl;
  157.     std::ofstream mp3_frames("mp3_test_output.mp3", std::ios::out | std::ios::binary);
  158.     mp3_frames.write((char*)mp3_frames_no_extra_space_ptr, result); // mp3_frames.write((char*)mp3_frames_ptr, buffer_size);
  159.  
  160.     // Flush the buffer.
  161.     int flush_result = lame_encode_flush(gfp, mp3_frames_no_extra_space_ptr, result);
  162.     std::cout << "LAME flushed buffer and output an additional " << flush_result << " bytes to mp3 file." << std::endl;
  163.  
  164.     std::cout << "Finishing up ... " << std::endl;
  165.     // Not writing the Xing VBR/INFO tag to the mp3 file. See initialization for call that goes with this one.
  166.     lame_mp3_tags_fid(gfp, NULL);
  167.  
  168.     // Free data structures.
  169.     lame_close(gfp);
  170.  
  171.     std::cout << "MP3 file complete." << std::endl;
  172.  
  173.     return 0;
  174. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement