wavopenal.pas 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330
  1. (* WavOpenAL - OpenAL wave playing example
  2. Copyright (c) 2010 Dmitry Boyarintsev
  3. This software is provided 'as-is', without any express or implied
  4. warranty. In no event will the authors be held liable for any damages
  5. arising from the use of this software.
  6. Permission is granted to anyone to use this software for any purpose,
  7. including commercial applications, and to alter it and redistribute it
  8. freely, subject to the following restrictions:
  9. 1. The origin of this software must not be misrepresented; you must not
  10. claim that you wrote the original software. If you use this software
  11. in a product, an acknowledgment in the product documentation would be
  12. appreciated but is not required.
  13. 2. Altered source versions must be plainly marked as such, and must not be
  14. misrepresented as being the original software.
  15. 3. This notice may not be removed or altered from any source
  16. distribution.
  17. WaveOpenAL is based on MadOpenAL playing sample.
  18. Yhe wavopenal program accepts a single .wav file name as a parameter and
  19. plays it using openal until the end.
  20. *)
  21. program wavopenal;
  22. {$mode objfpc}
  23. uses
  24. classes, sysutils, openal;
  25. // WAVE UTILS
  26. type
  27. TRiffHeader = packed record
  28. ID : array [0..3] of char;
  29. Size : LongWord;
  30. Format : array [0..3] of char;
  31. end;
  32. TWaveFormat = packed record
  33. ID : array [0..3] of char;
  34. Size : LongWord;
  35. Format : Word;
  36. Channels : Word;
  37. SampleRate : LongWord;
  38. ByteRate : LongWord;
  39. BlockAlign : Word;
  40. BitsPerSample : Word;
  41. end;
  42. TDataChunk = packed record
  43. Id : array [0..3] of char;
  44. Size : LongWord;
  45. end;
  46. type
  47. { TWaveReader }
  48. TWaveReader = class(TObject)
  49. private
  50. loaded : Boolean;
  51. chunkdata : TDataChunk;
  52. chunkpos : Int64;
  53. pos : Int64;
  54. eof : Boolean;
  55. fStream : TStream;
  56. public
  57. fmt : TWaveFormat;
  58. function LoadFromStream(AStream: TStream): Boolean;
  59. function ReadBuf(var Buffer; BufferSize: Integer): Integer;
  60. end;
  61. const
  62. ID_RIFF = 'RIFF';
  63. ID_WAVE = 'WAVE';
  64. ID_fmt = 'fmt ';
  65. ID_data = 'data';
  66. { TWaveReader }
  67. function TWaveReader.LoadFromStream(AStream:TStream):Boolean;
  68. var
  69. riff : TRiffHeader;
  70. begin
  71. fStream:=AStream;
  72. loaded:=True;
  73. try
  74. Result:=fStream.Read(riff, sizeof(riff))=sizeof(riff);
  75. riff.Size:=LEtoN(riff.Size);
  76. Result:=Result and (riff.ID=ID_RIFF) and (riff.Format=ID_WAVE);
  77. if not Result then Exit;
  78. Result:=fStream.Read(fmt, sizeof(fmt))=sizeof(fmt);
  79. fmt.Size:=LEtoN(fmt.Size);
  80. fmt.Format:=LEtoN(fmt.Format);
  81. fmt.Channels:=LEtoN(fmt.Channels);
  82. fmt.SampleRate:=LEtoN(fmt.SampleRate);
  83. fmt.ByteRate:=LEtoN(fmt.ByteRate);
  84. fmt.BlockAlign:=LEtoN(fmt.BlockAlign);
  85. fmt.BitsPerSample:=LEtoN(fmt.BitsPerSample);
  86. Result:=fmt.ID=ID_fmt;
  87. pos:=-1;
  88. except
  89. Result:=False;
  90. Exit;
  91. end;
  92. end;
  93. function Min(a,b: Integer): Integer;
  94. begin
  95. if a<b then Result:=a
  96. else Result:=b;
  97. end;
  98. function TWaveReader.ReadBuf(var Buffer;BufferSize:Integer):Integer;
  99. var
  100. sz : Integer;
  101. p : PByteArray;
  102. i : Integer;
  103. begin
  104. FillChar(Buffer, BufferSize, 0);
  105. Result:=0;
  106. // all data read
  107. if eof then Exit;
  108. p:=@Buffer;
  109. i:=0;
  110. while (not eof) and (i<bufferSize) do begin
  111. if chunkpos>=chunkdata.Size then begin
  112. if pos<0 then
  113. fstream.Position:=sizeof(TRiffHeader)+Int64(fmt.Size)+sizeof(TDataChunk)
  114. else
  115. fstream.Position:=pos+chunkdata.size+SizeOf(chunkdata);
  116. eof:=pos>=fStream.Size;
  117. if not eof then begin
  118. pos:=fStream.Position;
  119. sz:=fstream.Read(chunkdata, sizeof(chunkdata));
  120. chunkdata.Size:=LEtoN(chunkdata.Size);
  121. if (sz<>sizeof(chunkdata)) or (chunkdata.Id<>ID_data) then
  122. chunkpos:=chunkdata.Size
  123. else
  124. chunkpos:=0;
  125. end;
  126. end else begin
  127. sz:=Min(BufferSize, chunkdata.Size-chunkpos);
  128. fStream.Position:=pos+sizeof(chunkdata)+chunkpos;
  129. sz:=fStream.Read(p[i], sz);
  130. if sz<0 then Exit;
  131. inc(chunkpos, sz);
  132. inc(i, sz);
  133. end;
  134. end;
  135. Result:=i;
  136. end;
  137. // ------------------------ OPEN AL ----------------------
  138. var
  139. source : TStream;
  140. codec_bs : Longword;
  141. // openal
  142. const
  143. // Note: if you lower the al_bufcount, then you have to modify the al_polltime also!
  144. al_bufcount = 4;
  145. al_polltime = 100;
  146. var
  147. al_device : PALCdevice;
  148. al_context : PALCcontext;
  149. al_source : ALuint;
  150. al_format : Integer;
  151. al_buffers : array[0..al_bufcount-1] of ALuint;
  152. al_bufsize : Longword;
  153. al_readbuf : Pointer;
  154. al_rate : Longword;
  155. wave : TWaveReader;
  156. procedure alPlay;
  157. var
  158. i: Integer;
  159. begin
  160. alSourceStop(al_source);
  161. alSourceRewind(al_source);
  162. alSourcei(al_source, AL_BUFFER, 0);
  163. for i := 0 to al_bufcount - 1 do
  164. begin
  165. if wave.ReadBuf(al_readbuf^, al_bufsize) = 0 then
  166. Break;
  167. alBufferData(al_buffers[i], al_format, al_readbuf, al_bufsize, al_rate);
  168. alSourceQueueBuffers(al_source, 1, @al_buffers[i]);
  169. end;
  170. // Under windows, AL_LOOPING = AL_TRUE breaks queueing, no idea why
  171. alSourcei(al_source, AL_LOOPING, AL_FALSE);
  172. alSourcePlay(al_source);
  173. end;
  174. procedure alStop;
  175. begin
  176. alSourceStop(al_source);
  177. alSourceRewind(al_source);
  178. alSourcei(al_source, AL_BUFFER, 0);
  179. end;
  180. function alProcess: Boolean;
  181. var
  182. processed : ALint;
  183. buffer : ALuint;
  184. sz : Integer;
  185. begin
  186. alGetSourcei(al_source, AL_BUFFERS_PROCESSED, processed);
  187. while (processed > 0) and (processed <= al_bufcount) do
  188. begin
  189. Write('.');
  190. alSourceUnqueueBuffers(al_source, 1, @buffer);
  191. sz:=wave.ReadBuf(al_readbuf^, al_bufsize);
  192. if sz <= 0 then
  193. begin
  194. Exit(False);
  195. end;
  196. alBufferData(buffer, al_format, al_readbuf, sz, al_rate);
  197. alSourceQueueBuffers(al_source, 1, @buffer);
  198. Dec(processed);
  199. end;
  200. Result := True;
  201. end;
  202. var
  203. Filename: String;
  204. queued : Integer;
  205. done : Boolean;
  206. begin
  207. // define codec
  208. if (ParamCount<=0) or not FileExists(ParamStr(1)) then begin
  209. writeln('please specify .wav file name');
  210. Exit;
  211. end;
  212. FileName:=ParamStr(1);
  213. source := TFileStream.Create(Filename, fmOpenRead);
  214. // inittialize codec
  215. wave:=TWaveReader.Create;
  216. if not wave.LoadFromStream(source) then begin
  217. writeln('unable to read WAVE format');
  218. Exit;
  219. end;
  220. if wave.fmt.Format<>1 then begin
  221. writeln('WAVE file is using compression. Cannot play sorry. Please provide uncompressed .wav');
  222. Exit;
  223. end;
  224. if wave.fmt.Channels=2 then begin
  225. if wave.fmt.BitsPerSample=8 then al_format:=AL_FORMAT_STEREO8
  226. else al_format:=AL_FORMAT_STEREO16
  227. end else begin
  228. if wave.fmt.BitsPerSample=8 then al_format:=AL_FORMAT_MONO8
  229. else al_format:=AL_FORMAT_MONO16
  230. end;
  231. codec_bs:=2*wave.fmt.Channels;
  232. //al_bufsize := 20000 - (20000 mod codec_bs);
  233. al_bufsize := 20000 - (20000 mod codec_bs);
  234. al_rate:=wave.fmt.SampleRate;
  235. WriteLn('Blocksize : ', codec_bs);
  236. WriteLn('Rate : ', wave.fmt.SampleRate);
  237. WriteLn('Channels : ', wave.fmt.Channels);
  238. WriteLn('OpenAL Buffers : ', al_bufcount);
  239. WriteLn('OpenAL Buffer Size : ', al_bufsize);
  240. // init openal
  241. al_device := alcOpenDevice(nil);
  242. al_context := alcCreateContext(al_device, nil);
  243. alcMakeContextCurrent(al_context);
  244. alDistanceModel(AL_INVERSE_DISTANCE_CLAMPED);
  245. alGenSources(1, @al_source);
  246. alGenBuffers(al_bufcount, @al_buffers);
  247. GetMem(al_readbuf, al_bufsize);
  248. // play loop
  249. alPlay;
  250. done:=False;
  251. queued:=0;
  252. repeat
  253. if alProcess then begin
  254. alGetSourcei(al_source, AL_BUFFERS_QUEUED, queued);
  255. done:=queued=0;
  256. end;
  257. Sleep(al_polltime);
  258. until done;
  259. alStop;
  260. // finalize openal
  261. alDeleteSources(1, @al_source);
  262. alDeleteBuffers(al_bufcount, @al_buffers);
  263. alcDestroyContext(al_context);
  264. alcCloseDevice(al_device);
  265. FreeMem(al_readbuf);
  266. // finalize codec
  267. wave.Free;
  268. // close file
  269. source.Free;
  270. end.