1 module ddsp.util.wave; 2 3 import dplug.core.file; 4 import dplug.core.nogc; 5 import std.stdio; 6 7 /// 8 /// Class for reading and extracting data from files in WAVE format. 9 /// Currently only 16bit mono files are supported 10 /// 11 class WaveFile 12 { 13 this(const(char)[] filename) nothrow @nogc 14 { 15 data = readFileWav(filename); 16 chunkDescriptor = cast(char[4])data[0..4]; 17 chunkSize = bytesToInt(data[4..8]); 18 subChunk1Size = bytesToInt(data[16..20]); 19 audioFormat = bytesToInt(data[20..22]); 20 numChannels = bytesToInt(data[22..24]); 21 sampleRate = bytesToInt(data[24..28]); 22 byteRate = bytesToInt(data[28..32]); 23 blockAlign = bytesToInt(data[32..34]); 24 bitsPerSample = bytesToInt(data[34..36]); 25 dataSubChunk = cast(char[4])data[36..40]; 26 subChunk2Size = bytesToInt(data[40..44]); 27 28 leftChannel = mallocSlice!float(chunkSize / (numChannels * blockAlign)); 29 uint sampleNum; 30 for(int i = 44; i < data.length; i += numChannels * blockAlign, ++sampleNum) 31 { 32 leftChannel[sampleNum] = convertSampleData(data[i..i+blockAlign]); 33 } 34 35 } 36 37 float[] getSampleData() nothrow @nogc {return leftChannel[];} 38 39 40 41 override string toString() 42 { 43 import std.conv; 44 return "Chunk Descriptor: " ~ to!string(chunkDescriptor) 45 ~ "\nChunk Size: " ~ to!string(chunkSize) 46 ~ "\nSub Chunk1 Size: " ~ to!string(subChunk1Size) 47 ~ "\nAudio Format: " ~ to!string(audioFormat) 48 ~ "\nNum Channels: " ~ to!string(numChannels) 49 ~ "\nSample Rate: " ~ to!string(sampleRate) 50 ~ "\nByte Rate: " ~ to!string(byteRate) 51 ~ "\nBlock Align: " ~ to!string(blockAlign) 52 ~ "\nBits Per Sample: " ~ to!string(bitsPerSample) 53 ~ "\nData Sub Chuck: " ~ to!string(bitsPerSample) 54 ~ "\nSub Chunk2 Size: " ~ to!string(subChunk2Size); 55 } 56 57 private: 58 59 //RIFF 60 char[4] chunkDescriptor; 61 uint chunkSize; 62 uint subChunk1Size; 63 uint audioFormat; 64 uint numChannels; 65 uint sampleRate; 66 uint byteRate; 67 uint blockAlign; 68 uint bitsPerSample; 69 char[4] dataSubChunk; 70 uint subChunk2Size; 71 byte b; 72 73 int sample1; 74 75 byte[] data; 76 float[] leftChannel; 77 float[] rightChannel; 78 79 int bytesToInt(byte[] bytes) nothrow @nogc 80 { 81 int sum = cast(ubyte)bytes[0]; 82 for(int i = 1; i < bytes.length; ++i) 83 { 84 sum |= cast(ubyte)bytes[i] << (i *8); 85 } 86 return sum; 87 } 88 89 float convertSampleData(byte[] bytes) nothrow @nogc 90 { 91 int sum = bytes[0]; 92 for(int i = 1; i < bytes.length; ++i){ 93 sum |= cast(int)bytes[i] << (8 * i); 94 } 95 /*//take two's compliment 96 0x8000 & sum ? sum = cast(int)(0x7FFF & sum) - 0x8000 : sum = sum; 97 return sum / 32768.0f;*/ 98 const float div = 1.0f / 32768f; 99 return cast(float)sum * div; 100 //return sum; 101 } 102 } 103 104 unittest 105 { 106 import std.stdio; 107 108 //WaveFile file = new WaveFile("util/ddsp/util/8bitexample.wav"); 109 //writeln(file.toString()); 110 //writeln(file.getSampleData()[0..1000]); 111 } 112 113 import core.stdc.stdio; 114 115 nothrow: 116 @nogc: 117 118 /// 119 /// Taken from dplug.core.file 120 /// Modified to return signed data instead of unsigned data. 121 /// 122 byte[] readFileWav(const(char)[] fileNameZ) 123 { 124 // assuming that fileNameZ is zero-terminated, since it will in practice be 125 // a static string 126 FILE* file = fopen(fileNameZ.ptr, "rb".ptr); 127 if (file) 128 { 129 scope(exit) fclose(file); 130 131 // finds the size of the file 132 fseek(file, 0, SEEK_END); 133 long size = ftell(file); 134 fseek(file, 0, SEEK_SET); 135 136 // Is this too large to read? 137 // Refuse to read more than 1gb file (if it happens, it's probably a bug). 138 if (size > 1024*1024*1024) 139 return null; 140 141 // Read whole file in a mallocated slice 142 byte[] fileBytes = mallocSliceNoInit!byte(cast(int)size); 143 size_t remaining = cast(size_t)size; 144 145 byte* p = fileBytes.ptr; 146 147 while (remaining > 0) 148 { 149 size_t bytesRead = fread(p, 1, remaining, file); 150 if (bytesRead == 0) 151 { 152 freeSlice(fileBytes); 153 return null; 154 } 155 p += bytesRead; 156 remaining -= bytesRead; 157 } 158 159 return fileBytes; 160 } 161 else 162 return null; 163 }