|
@@ -185,7 +185,8 @@ typedef enum {
|
|
|
// Module specific Functions Declaration
|
|
|
//----------------------------------------------------------------------------------
|
|
|
#if defined(SUPPORT_FILEFORMAT_WAV)
|
|
|
-static Wave LoadWAV(const char *fileName); // Load WAV file
|
|
|
+static Wave LoadWAV(const char *fileName); // Load WAV file
|
|
|
+static int SaveWAV(Wave wave, const char *fileName); // Save wave data as WAV file
|
|
|
#endif
|
|
|
#if defined(SUPPORT_FILEFORMAT_OGG)
|
|
|
static Wave LoadOGG(const char *fileName); // Load OGG file
|
|
@@ -878,82 +879,57 @@ void ExportWave(Wave wave, const char *fileName)
|
|
|
{
|
|
|
bool success = false;
|
|
|
|
|
|
- if (IsFileExtension(fileName, ".wav"))
|
|
|
+ if (IsFileExtension(fileName, ".wav")) success = SaveWAV(wave, fileName);
|
|
|
+ else if (IsFileExtension(fileName, ".raw"))
|
|
|
{
|
|
|
- // Basic WAV headers structs
|
|
|
- typedef struct {
|
|
|
- char chunkID[4];
|
|
|
- int chunkSize;
|
|
|
- char format[4];
|
|
|
- } RiffHeader;
|
|
|
-
|
|
|
- typedef struct {
|
|
|
- char subChunkID[4];
|
|
|
- int subChunkSize;
|
|
|
- short audioFormat;
|
|
|
- short numChannels;
|
|
|
- int sampleRate;
|
|
|
- int byteRate;
|
|
|
- short blockAlign;
|
|
|
- short bitsPerSample;
|
|
|
- } WaveFormat;
|
|
|
-
|
|
|
- typedef struct {
|
|
|
- char subChunkID[4];
|
|
|
- int subChunkSize;
|
|
|
- } WaveData;
|
|
|
+ // Export raw sample data (without header)
|
|
|
+ // NOTE: It's up to the user to track wave parameters
|
|
|
+ FILE *rawFile = fopen(fileName, "wb");
|
|
|
+ success = fwrite(wave.data, wave.sampleCount*wave.channels*wave.sampleSize/8, 1, rawFile);
|
|
|
+ fclose(rawFile);
|
|
|
+ }
|
|
|
|
|
|
- RiffHeader riffHeader;
|
|
|
- WaveFormat waveFormat;
|
|
|
- WaveData waveData;
|
|
|
-
|
|
|
- // Fill structs with data
|
|
|
- riffHeader.chunkID[0] = 'R';
|
|
|
- riffHeader.chunkID[1] = 'I';
|
|
|
- riffHeader.chunkID[2] = 'F';
|
|
|
- riffHeader.chunkID[3] = 'F';
|
|
|
- riffHeader.chunkSize = 44 - 4 + wave.sampleCount*wave.sampleSize/8;
|
|
|
- riffHeader.format[0] = 'W';
|
|
|
- riffHeader.format[1] = 'A';
|
|
|
- riffHeader.format[2] = 'V';
|
|
|
- riffHeader.format[3] = 'E';
|
|
|
-
|
|
|
- waveFormat.subChunkID[0] = 'f';
|
|
|
- waveFormat.subChunkID[1] = 'm';
|
|
|
- waveFormat.subChunkID[2] = 't';
|
|
|
- waveFormat.subChunkID[3] = ' ';
|
|
|
- waveFormat.subChunkSize = 16;
|
|
|
- waveFormat.audioFormat = 1;
|
|
|
- waveFormat.numChannels = wave.channels;
|
|
|
- waveFormat.sampleRate = wave.sampleRate;
|
|
|
- waveFormat.byteRate = wave.sampleRate*wave.sampleSize/8;
|
|
|
- waveFormat.blockAlign = wave.sampleSize/8;
|
|
|
- waveFormat.bitsPerSample = wave.sampleSize;
|
|
|
-
|
|
|
- waveData.subChunkID[0] = 'd';
|
|
|
- waveData.subChunkID[1] = 'a';
|
|
|
- waveData.subChunkID[2] = 't';
|
|
|
- waveData.subChunkID[3] = 'a';
|
|
|
- waveData.subChunkSize = wave.sampleCount*wave.channels*wave.sampleSize/8;
|
|
|
-
|
|
|
- FILE *wavFile = fopen(fileName, "wb");
|
|
|
-
|
|
|
- if (wavFile == NULL) return;
|
|
|
-
|
|
|
- fwrite(&riffHeader, 1, sizeof(RiffHeader), wavFile);
|
|
|
- fwrite(&waveFormat, 1, sizeof(WaveFormat), wavFile);
|
|
|
- fwrite(&waveData, 1, sizeof(WaveData), wavFile);
|
|
|
+ if (success) TraceLog(LOG_INFO, "Wave exported successfully: %s", fileName);
|
|
|
+ else TraceLog(LOG_WARNING, "Wave could not be exported.");
|
|
|
+}
|
|
|
|
|
|
- fwrite(wave.data, 1, wave.sampleCount*wave.channels*wave.sampleSize/8, wavFile);
|
|
|
+// Export wave sample data to code (.h)
|
|
|
+void ExportWaveAsCode(Wave wave, const char *fileName)
|
|
|
+{
|
|
|
+ #define BYTES_TEXT_PER_LINE 20
|
|
|
+
|
|
|
+ char varFileName[256] = { 0 };
|
|
|
+ int dataSize = wave.sampleCount*wave.channels*wave.sampleSize/8;
|
|
|
+
|
|
|
+ FILE *txtFile = fopen(fileName, "wt");
|
|
|
+
|
|
|
+ fprintf(txtFile, "\n//////////////////////////////////////////////////////////////////////////////////\n");
|
|
|
+ fprintf(txtFile, "// //\n");
|
|
|
+ fprintf(txtFile, "// WaveAsCode exporter v1.0 - Wave data exported as an array of bytes //\n");
|
|
|
+ fprintf(txtFile, "// //\n");
|
|
|
+ fprintf(txtFile, "// more info and bugs-report: github.com/raysan5/raylib //\n");
|
|
|
+ fprintf(txtFile, "// feedback and support: ray[at]raylib.com //\n");
|
|
|
+ fprintf(txtFile, "// //\n");
|
|
|
+ fprintf(txtFile, "// Copyright (c) 2018 Ramon Santamaria (@raysan5) //\n");
|
|
|
+ fprintf(txtFile, "// //\n");
|
|
|
+ fprintf(txtFile, "//////////////////////////////////////////////////////////////////////////////////\n\n");
|
|
|
+
|
|
|
+ // Get file name from path and convert variable name to uppercase
|
|
|
+ strcpy(varFileName, GetFileNameWithoutExt(fileName));
|
|
|
+ for (int i = 0; varFileName[i] != '\0'; i++) if (varFileName[i] >= 'a' && varFileName[i] <= 'z') { varFileName[i] = varFileName[i] - 32; }
|
|
|
+
|
|
|
+ fprintf(txtFile, "// Wave data information\n");
|
|
|
+ fprintf(txtFile, "#define %s_SAMPLE_COUNT %i\n", varFileName, wave.sampleCount);
|
|
|
+ fprintf(txtFile, "#define %s_SAMPLE_RATE %i\n", varFileName, wave.sampleRate);
|
|
|
+ fprintf(txtFile, "#define %s_SAMPLE_SIZE %i\n", varFileName, wave.sampleSize);
|
|
|
+ fprintf(txtFile, "#define %s_CHANNELS %i\n\n", varFileName, wave.channels);
|
|
|
|
|
|
- fclose(wavFile);
|
|
|
-
|
|
|
- success = true;
|
|
|
- }
|
|
|
- else if (IsFileExtension(fileName, ".raw")) { } // TODO: Support additional file formats to export wave sample data
|
|
|
+ // Write byte data as hexadecimal text
|
|
|
+ fprintf(txtFile, "static unsigned char %s_DATA[%i] = { ", varFileName, dataSize);
|
|
|
+ for (int i = 0; i < dataSize - 1; i++) fprintf(txtFile, ((i%BYTES_TEXT_PER_LINE == 0) ? "0x%x,\n" : "0x%x, "), ((unsigned char *)wave.data)[i]);
|
|
|
+ fprintf(txtFile, "0x%x };\n", ((unsigned char *)wave.data)[dataSize - 1]);
|
|
|
|
|
|
- if (success) TraceLog(LOG_INFO, "Wave exported successfully: %s", fileName);
|
|
|
- else TraceLog(LOG_WARNING, "Wave could not be exported.");
|
|
|
+ fclose(txtFile);
|
|
|
}
|
|
|
|
|
|
// Play a sound
|
|
@@ -1739,6 +1715,86 @@ static Wave LoadWAV(const char *fileName)
|
|
|
|
|
|
return wave;
|
|
|
}
|
|
|
+
|
|
|
+// Save wave data as WAV file
|
|
|
+static int SaveWAV(Wave wave, const char *fileName)
|
|
|
+{
|
|
|
+ int success = 0;
|
|
|
+ int dataSize = wave.sampleCount*wave.channels*wave.sampleSize/8;
|
|
|
+
|
|
|
+ // Basic WAV headers structs
|
|
|
+ typedef struct {
|
|
|
+ char chunkID[4];
|
|
|
+ int chunkSize;
|
|
|
+ char format[4];
|
|
|
+ } RiffHeader;
|
|
|
+
|
|
|
+ typedef struct {
|
|
|
+ char subChunkID[4];
|
|
|
+ int subChunkSize;
|
|
|
+ short audioFormat;
|
|
|
+ short numChannels;
|
|
|
+ int sampleRate;
|
|
|
+ int byteRate;
|
|
|
+ short blockAlign;
|
|
|
+ short bitsPerSample;
|
|
|
+ } WaveFormat;
|
|
|
+
|
|
|
+ typedef struct {
|
|
|
+ char subChunkID[4];
|
|
|
+ int subChunkSize;
|
|
|
+ } WaveData;
|
|
|
+
|
|
|
+ FILE *wavFile = fopen(fileName, "wb");
|
|
|
+
|
|
|
+ if (wavFile == NULL) TraceLog(LOG_WARNING, "[%s] WAV audio file could not be created", fileName);
|
|
|
+ else
|
|
|
+ {
|
|
|
+ RiffHeader riffHeader;
|
|
|
+ WaveFormat waveFormat;
|
|
|
+ WaveData waveData;
|
|
|
+
|
|
|
+ // Fill structs with data
|
|
|
+ riffHeader.chunkID[0] = 'R';
|
|
|
+ riffHeader.chunkID[1] = 'I';
|
|
|
+ riffHeader.chunkID[2] = 'F';
|
|
|
+ riffHeader.chunkID[3] = 'F';
|
|
|
+ riffHeader.chunkSize = 44 - 4 + wave.sampleCount*wave.sampleSize/8;
|
|
|
+ riffHeader.format[0] = 'W';
|
|
|
+ riffHeader.format[1] = 'A';
|
|
|
+ riffHeader.format[2] = 'V';
|
|
|
+ riffHeader.format[3] = 'E';
|
|
|
+
|
|
|
+ waveFormat.subChunkID[0] = 'f';
|
|
|
+ waveFormat.subChunkID[1] = 'm';
|
|
|
+ waveFormat.subChunkID[2] = 't';
|
|
|
+ waveFormat.subChunkID[3] = ' ';
|
|
|
+ waveFormat.subChunkSize = 16;
|
|
|
+ waveFormat.audioFormat = 1;
|
|
|
+ waveFormat.numChannels = wave.channels;
|
|
|
+ waveFormat.sampleRate = wave.sampleRate;
|
|
|
+ waveFormat.byteRate = wave.sampleRate*wave.sampleSize/8;
|
|
|
+ waveFormat.blockAlign = wave.sampleSize/8;
|
|
|
+ waveFormat.bitsPerSample = wave.sampleSize;
|
|
|
+
|
|
|
+ waveData.subChunkID[0] = 'd';
|
|
|
+ waveData.subChunkID[1] = 'a';
|
|
|
+ waveData.subChunkID[2] = 't';
|
|
|
+ waveData.subChunkID[3] = 'a';
|
|
|
+ waveData.subChunkSize = dataSize;
|
|
|
+
|
|
|
+ success = fwrite(&riffHeader, sizeof(RiffHeader), 1, wavFile);
|
|
|
+ success = fwrite(&waveFormat, sizeof(WaveFormat), 1, wavFile);
|
|
|
+ success = fwrite(&waveData, sizeof(WaveData), 1, wavFile);
|
|
|
+
|
|
|
+ success = fwrite(wave.data, dataSize, 1, wavFile);
|
|
|
+
|
|
|
+ fclose(wavFile);
|
|
|
+ }
|
|
|
+
|
|
|
+ // If all data has been written correctly to file, success = 1
|
|
|
+ return success;
|
|
|
+}
|
|
|
#endif
|
|
|
|
|
|
#if defined(SUPPORT_FILEFORMAT_OGG)
|