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 }