1 /*
2  * libPCM by László Szerémi.
3  * Copyright under Boost License.
4  */
5 
6 module libPCM.file;
7 
8 import core.stdc.stdio;
9 import core.stdc.stdlib;
10 import std.stdio;
11 version(Windows){
12 	import core.sys.windows.windows;
13 }else version(Posix){
14 	import core.stdc.errno;
15 }
16 
17 import libPCM.types;
18 import libPCM.common;
19 import libPCM.utility;
20 
21 public:
22 	/**
23 	 * Loads a *.pcm file into the memory.
24 	 */
25 	PCMFile loadPCMFile(immutable char* name, bool indivStreams = true){
26 		FILE* inputStream = fopen(name, "rb");
27 		if(inputStream is null){
28 			import std.conv;
29 			version(Windows){
30 				DWORD errorCode = GetLastError();
31 			}else version(Posix){
32 				int errorCode = errno;
33 			}
34 
35 			throw new AudioFileException("File access error! Error number: " ~ to!string(errorCode));
36 		}
37 		PCMFile file;
38 		void* buffer;
39 		char* tagData;
40 		fread(&file.header, PCMHeader.sizeof, 1, inputStream);
41 		size_t tagData_l = (file.header.author_l + file.header.comment_l + file.header.copyright_l + file.header.name_l) * 4;
42 		if(tagData_l){
43 			tagData = cast(char*)malloc(tagData_l);
44 			fread(tagData, tagData_l, 1, inputStream);
45 			file.loadTagData(tagData);
46 			free(tagData);
47 		}
48 		if(indivStreams){
49 			int wordlength = getWordLength(file.header.codecType);
50 			size_t sampleSize = (file.header.length * wordlength) / 8;
51 			sampleSize += (file.header.length * wordlength) % 8 ? 1 : 0;
52 			//allocate memory for the individual WaveData
53 			for(int i ; i < file.header.numOfChannels ; i++){
54 				
55 				file.waveData ~= new WaveData(file.header.length, file.header.sampleRate, file.header.codecType, sampleSize);
56 			}
57 			buffer = malloc(sampleSize * file.header.numOfChannels);
58 			fread(buffer, sampleSize, file.header.numOfChannels, inputStream);
59 			size_t bufferOffset;
60 			switch(wordlength){
61 				case 16:
62 					for(size_t i ; i < sampleSize; i++){
63 						for(int j ; j < file.header.numOfChannels ; j++){
64 							*cast(ushort*)((file.waveData[j].data.ptr) + i) = *(cast(ushort*)(buffer) + bufferOffset);
65 							bufferOffset++;
66 						}
67 					}
68 					break;
69 				case 24, 12:
70 					for(size_t i ; i < sampleSize; i++){
71 						for(int j ; j < file.header.numOfChannels ; j++){
72 							*cast(ubyte*)((file.waveData[j].data.ptr) + i) = *cast(ubyte*)(buffer + bufferOffset);
73 							bufferOffset++;
74 							*cast(ubyte*)((file.waveData[j].data.ptr) + i) = *cast(ubyte*)(buffer + bufferOffset);
75 							bufferOffset++;
76 							*cast(ubyte*)((file.waveData[j].data.ptr) + i) = *cast(ubyte*)(buffer + bufferOffset);
77 							bufferOffset++;
78 						}
79 					}
80 					break;
81 				case 32:
82 					for(size_t i ; i < sampleSize; i++){
83 						for(int j ; j < file.header.numOfChannels ; j++){
84 							*cast(uint*)((file.waveData[j].data.ptr) + i) = *(cast(uint*)(buffer) + bufferOffset);
85 							bufferOffset++;
86 						}
87 					}
88 					break;
89 				default:
90 					for(size_t i ; i < sampleSize; i++){
91 						for(int j ; j < file.header.numOfChannels ; j++){
92 							*cast(ubyte*)((file.waveData[j].data.ptr) + i) = *cast(ubyte*)(buffer + bufferOffset);
93 							bufferOffset++;
94 						}
95 					}
96 					break;
97 			}
98 			free(buffer);
99 		}else{
100 			int wordlength = getWordLength(file.header.codecType);
101 			size_t sampleSize = (file.header.length * wordlength) / 8;
102 			sampleSize += (file.header.length * wordlength) % 8 ? 1 : 0;
103 			buffer = malloc(sampleSize * file.header.numOfChannels);
104 			fread(buffer, sampleSize, file.header.numOfChannels, inputStream);
105 			memCpy(file.startOfData.ptr, buffer, sampleSize * file.header.numOfChannels);
106 			free(buffer);
107 		}
108 		fclose(inputStream);
109 		return file;
110 	}
111 	/**
112 	 * Loads a *.wav file into the memory.
113 	 */
114 	WavFile loadWavFile(immutable char* name, bool indivStreams = true){
115 		FILE* inputStream = fopen(name, "rb");
116 		if(inputStream is null){
117 			import std.conv;
118 			version(Windows){
119 				DWORD errorCode = GetLastError();
120 			}else version(Posix){
121 				int errorCode = errno;
122 			}
123 			throw new AudioFileException("File access error! Error number: " ~ to!string(errorCode));
124 		}
125 		WavFile file;
126 		WavHeader header;
127 		void* buffer;
128 		fread(&header, header.sizeof, 1, inputStream);
129 		file.header = header;
130 		buffer = malloc(file.header.subchunk2Size);
131 		fread(buffer, file.header.subchunk2Size, 1, inputStream);
132 		if(!indivStreams){
133 			memCpy(file.startOfData.ptr, buffer, file.header.subchunk2Size);
134 			free(buffer);
135 			return file;
136 		}
137 		file.waveData.length = file.header.numOfChannels;
138 		if(file.header.numOfChannels == 1){
139 			CodecType codecType;
140 			if(file.header.bitsPerSample == 8 && file.header.audioFormat == 1){
141 				codecType = CodecType.UNSIGNED8BIT;
142 			}else if(file.header.bitsPerSample == 16 && file.header.audioFormat == 1){
143 				codecType = CodecType.SIGNED16BIT;
144 			}
145 			file.waveData[0] = new WaveData(file.header.subchunk2Size * file.header.bitsPerSample / 8, file.header.sampleRate, codecType, file.header.subchunk2Size);
146 			memCpy(buffer, file.waveData[0].data.ptr, file.header.subchunk2Size);
147 		}else if(file.header.numOfChannels == 2){
148 			CodecType codecType;
149 			if(file.header.bitsPerSample == 8 && file.header.audioFormat == 1){
150 				codecType = CodecType.UNSIGNED8BIT;
151 			}else if(file.header.bitsPerSample == 16 && file.header.audioFormat == 1){
152 				codecType = CodecType.SIGNED16BIT;
153 			}
154 			file.waveData[0] = new WaveData(file.header.subchunk2Size * file.header.bitsPerSample / 16, file.header.sampleRate, codecType, file.header.subchunk2Size);
155 			file.waveData[1] = new WaveData(file.header.subchunk2Size * file.header.bitsPerSample / 16, file.header.sampleRate, codecType, file.header.subchunk2Size);
156 			size_t bufferOffset;
157 			if(file.header.bitsPerSample == 8 && file.header.audioFormat == 1){
158 				for(size_t i ; i < file.waveData[0].length; i++){
159 					for(int j ; j < file.header.numOfChannels ; j++){
160 						*cast(ubyte*)(file.waveData[j].data.ptr + i) = *cast(ubyte*)(buffer + bufferOffset);
161 						bufferOffset++;
162 					}
163 				}
164 			}else if(file.header.bitsPerSample == 16 && file.header.audioFormat == 1){
165 				for(size_t i ; i < file.waveData[0].length ; i++){
166 					for(int j ; j < file.header.numOfChannels ; j++){
167 						*cast(ushort*)(file.waveData[j].data.ptr + i) = *cast(ushort*)(buffer + bufferOffset);
168 						bufferOffset++;
169 					}
170 				}
171 			}
172 		}
173 		fclose(inputStream);
174 		free(buffer);
175 		return file;
176 	}
177 	/**
178 	 * Stores a *.wav file.
179 	 */
180 	void storeWavFile(WavFile file, immutable char* name){
181 		FILE* outputStream = fopen(name, "wb");
182 		if(outputStream is null){
183 			import std.conv;
184 			version(Windows){
185 				DWORD errorCode = GetLastError();
186 			}else version(Posix){
187 				int errorCode = errno;
188 			}
189 			throw new AudioFileException("File access error! Error number: " ~ to!string(errorCode));
190 		}
191 		fwrite(&(file.header), WavHeader.sizeof, 1, outputStream);
192 		if(file.startOfData.length){
193 			fwrite(file.startOfData.ptr, file.startOfData.length, 1, outputStream);
194 		}else if(file.waveData.length == 1){
195 			fwrite(file.waveData[0].data.ptr, file.waveData[0].data.length, 1, outputStream);
196 		}
197 		fclose(outputStream);
198 	}
199 	/**
200 	 * Stores a *.pcm file.
201 	 */
202 	void storePCMFile(PCMFile file, immutable char* name){
203 		FILE* outputStream = fopen(name, "wb");
204 		if(outputStream is null){
205 			import std.conv;
206 			version(Windows){
207 				DWORD errorCode = GetLastError();
208 			}else version(Posix){
209 				int errorCode = errno;
210 			}
211 			throw new AudioFileException("File access error! Error number: " ~ to!string(errorCode));
212 		}
213 		fwrite(&(file.header), PCMHeader.sizeof, 1, outputStream);
214 		if(file.startOfData.length){
215 			fwrite(file.startOfData.ptr, file.startOfData.length, 1, outputStream);
216 		}else if(file.waveData.length == 1){
217 			fwrite(file.waveData[0].data.ptr, file.waveData[0].data.length, 1, outputStream);
218 		}
219 		fclose(outputStream);
220 	}