2
0

GLS.SoundFileObjects.pas 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340
  1. //
  2. // The multimedia graphics platform GLScene https://github.com/glscene
  3. //
  4. unit GLS.SoundFileObjects;
  5. (*
  6. Support classes for loading various fileformats.
  7. These classes work together like vector file formats or Delphi's TGraphic classes.
  8. *)
  9. interface
  10. {$I GLScene.inc}
  11. uses
  12. Winapi.MMSystem,
  13. System.Classes,
  14. System.SysUtils,
  15. VCL.Consts,
  16. GLS.ApplicationFileIO;
  17. type
  18. // Defines a sound sampling quality.
  19. TGLSoundSampling = class(TPersistent)
  20. private
  21. FOwner: TPersistent;
  22. FFrequency: Integer;
  23. FNbChannels: Integer;
  24. FBitsPerSample: Integer;
  25. protected
  26. function GetOwner: TPersistent; override;
  27. public
  28. constructor Create(AOwner: TPersistent);
  29. destructor Destroy; override;
  30. procedure Assign(Source: TPersistent); override;
  31. function BytesPerSec: Integer;
  32. function BytesPerSample: Integer;
  33. function WaveFormat: TWaveFormatEx;
  34. published
  35. // Sampling frequency in Hz (= samples per sec)
  36. property Frequency: Integer read FFrequency write FFrequency default 22050;
  37. (* Nb of sampling channels.
  38. 1 = mono, 2 = stereo, etc. *)
  39. property NbChannels: Integer read FNbChannels write FNbChannels default 1;
  40. (* Nb of bits per sample.
  41. Common values are 8 and 16 bits. *)
  42. property BitsPerSample: Integer read FBitsPerSample write FBitsPerSample default 8;
  43. end;
  44. (* Abstract base class for different Sound file formats.
  45. The actual implementation for these files (WAV, RAW...) must be done
  46. seperately. The concept for TGLSoundFile is very similar to TGraphic
  47. (see Delphi Help).
  48. Default implementation for LoadFromFile/SaveToFile are to directly call the
  49. relevent stream-based methods, ie. you will just have to override the stream
  50. methods in most cases. *)
  51. TGLSoundFile = class(TGLDataFile)
  52. private
  53. FSampling: TGLSoundSampling;
  54. protected
  55. procedure SetSampling(const val: TGLSoundSampling);
  56. public
  57. constructor Create(AOwner: TPersistent); override;
  58. destructor Destroy; override;
  59. procedure PlayOnWaveOut; virtual;
  60. // Returns a pointer to the sample data viewed as an in-memory WAV File.
  61. function WAVData: Pointer; virtual; abstract;
  62. // Returns the size (in bytes) of the WAVData.
  63. function WAVDataSize: Integer; virtual; abstract;
  64. // Returns a pointer to the sample data viewed as an in-memory PCM buffer.
  65. function PCMData: Pointer; virtual; abstract;
  66. // Length of PCM data, in bytes.
  67. function LengthInBytes: Integer; virtual; abstract;
  68. // Nb of intensity samples in the sample.
  69. function LengthInSamples: Integer;
  70. // Length of play of the sample at nominal speed in seconds.
  71. function LengthInSec: Single;
  72. property Sampling: TGLSoundSampling read FSampling write SetSampling;
  73. end;
  74. TGLSoundFileClass = class of TGLSoundFile;
  75. TGLSoundFileFormat = record
  76. SoundFileClass: TGLSoundFileClass;
  77. Extension: String;
  78. Description: String;
  79. DescResID: Integer;
  80. end;
  81. PSoundFileFormat = ^TGLSoundFileFormat;
  82. TGLSoundFileFormatsList = class(TList)
  83. public
  84. destructor Destroy; override;
  85. procedure Add(const Ext, Desc: String; DescID: Integer;
  86. AClass: TGLSoundFileClass);
  87. function FindExt(Ext: string): TGLSoundFileClass;
  88. procedure Remove(AClass: TGLSoundFileClass);
  89. procedure BuildFilterStrings(SoundFileClass: TGLSoundFileClass;
  90. out Descriptions, Filters: string);
  91. end;
  92. function GetGLSoundFileFormats: TGLSoundFileFormatsList;
  93. procedure RegisterSoundFileFormat(const AExtension, ADescription: String;
  94. AClass: TGLSoundFileClass);
  95. procedure UnregisterSoundFileClass(AClass: TGLSoundFileClass);
  96. // ------------------------------------------------------------------
  97. implementation
  98. // ------------------------------------------------------------------
  99. var
  100. vSoundFileFormats: TGLSoundFileFormatsList;
  101. function GetGLSoundFileFormats: TGLSoundFileFormatsList;
  102. begin
  103. if not Assigned(vSoundFileFormats) then
  104. vSoundFileFormats := TGLSoundFileFormatsList.Create;
  105. Result := vSoundFileFormats;
  106. end;
  107. procedure RegisterSoundFileFormat(const AExtension, ADescription: String;
  108. AClass: TGLSoundFileClass);
  109. begin
  110. RegisterClass(AClass);
  111. GetGLSoundFileFormats.Add(AExtension, ADescription, 0, AClass);
  112. end;
  113. procedure UnregisterSoundFileClass(AClass: TGLSoundFileClass);
  114. begin
  115. if Assigned(vSoundFileFormats) then
  116. vSoundFileFormats.Remove(AClass);
  117. end;
  118. // ------------------
  119. // ------------------ TGLSoundSampling ------------------
  120. // ------------------
  121. constructor TGLSoundSampling.Create(AOwner: TPersistent);
  122. begin
  123. inherited Create;
  124. FOwner := AOwner;
  125. FFrequency := 22050;
  126. FNbChannels := 1;
  127. FBitsPerSample := 8;
  128. end;
  129. destructor TGLSoundSampling.Destroy;
  130. begin
  131. inherited Destroy;
  132. end;
  133. procedure TGLSoundSampling.Assign(Source: TPersistent);
  134. begin
  135. if Source is TGLSoundSampling then
  136. begin
  137. FFrequency := TGLSoundSampling(Source).Frequency;
  138. FNbChannels := TGLSoundSampling(Source).NbChannels;
  139. FBitsPerSample := TGLSoundSampling(Source).BitsPerSample;
  140. end
  141. else
  142. inherited;
  143. end;
  144. function TGLSoundSampling.GetOwner: TPersistent;
  145. begin
  146. Result := FOwner;
  147. end;
  148. function TGLSoundSampling.BytesPerSec: Integer;
  149. begin
  150. Result := (FFrequency * FBitsPerSample * FNbChannels) shr 3;
  151. end;
  152. function TGLSoundSampling.BytesPerSample: Integer;
  153. begin
  154. Result := FBitsPerSample shr 3;
  155. end;
  156. function TGLSoundSampling.WaveFormat: TWaveFormatEx;
  157. begin
  158. Result.nSamplesPerSec := Frequency;
  159. Result.nChannels := NbChannels;
  160. Result.wFormatTag := Wave_Format_PCM;
  161. Result.nAvgBytesPerSec := BytesPerSec;
  162. Result.wBitsPerSample := BitsPerSample;
  163. Result.nBlockAlign := NbChannels * BytesPerSample;
  164. Result.cbSize := 0;
  165. end;
  166. // ------------------
  167. // ------------------ TGLSoundFile ------------------
  168. // ------------------
  169. constructor TGLSoundFile.Create(AOwner: TPersistent);
  170. begin
  171. inherited;
  172. FSampling := TGLSoundSampling.Create(Self);
  173. end;
  174. destructor TGLSoundFile.Destroy;
  175. begin
  176. FSampling.Free;
  177. inherited;
  178. end;
  179. procedure TGLSoundFile.SetSampling(const val: TGLSoundSampling);
  180. begin
  181. FSampling.Assign(val);
  182. end;
  183. procedure TGLSoundFile.PlayOnWaveOut;
  184. begin
  185. // GLSM.SoundFileObjects.PlayOnWaveOut(PCMData, LengthInSamples, Sampling);
  186. end;
  187. function TGLSoundFile.LengthInSamples: Integer;
  188. var
  189. d: Integer;
  190. begin
  191. d := Sampling.BytesPerSample * Sampling.NbChannels;
  192. if d > 0 then
  193. Result := LengthInBytes div d
  194. else
  195. Result := 0;
  196. end;
  197. function TGLSoundFile.LengthInSec: Single;
  198. begin
  199. Result := LengthInBytes / Sampling.BytesPerSec;
  200. end;
  201. // ------------------
  202. // ------------------ TGLSoundFileFormatsList ------------------
  203. // ------------------
  204. destructor TGLSoundFileFormatsList.Destroy;
  205. var
  206. i: Integer;
  207. begin
  208. for i := 0 to Count - 1 do
  209. Dispose(PSoundFileFormat(Items[i]));
  210. inherited;
  211. end;
  212. procedure TGLSoundFileFormatsList.Add(const Ext, Desc: String; DescID: Integer;
  213. AClass: TGLSoundFileClass);
  214. var
  215. newRec: PSoundFileFormat;
  216. begin
  217. New(newRec);
  218. with newRec^ do
  219. begin
  220. Extension := AnsiLowerCase(Ext);
  221. SoundFileClass := AClass;
  222. Description := Desc;
  223. DescResID := DescID;
  224. end;
  225. inherited Add(newRec);
  226. end;
  227. function TGLSoundFileFormatsList.FindExt(Ext: string): TGLSoundFileClass;
  228. var
  229. i: Integer;
  230. begin
  231. Ext := AnsiLowerCase(Ext);
  232. for i := Count - 1 downto 0 do
  233. with PSoundFileFormat(Items[i])^ do
  234. if (Extension = Ext) or ('.' + Extension = Ext) then
  235. begin
  236. Result := SoundFileClass;
  237. Exit;
  238. end;
  239. Result := nil;
  240. end;
  241. procedure TGLSoundFileFormatsList.Remove(AClass: TGLSoundFileClass);
  242. var
  243. i: Integer;
  244. p: PSoundFileFormat;
  245. begin
  246. for i := Count - 1 downto 0 do
  247. begin
  248. p := PSoundFileFormat(Items[i]);
  249. if p^.SoundFileClass.InheritsFrom(AClass) then
  250. begin
  251. Dispose(p);
  252. Delete(i);
  253. end;
  254. end;
  255. end;
  256. procedure TGLSoundFileFormatsList.BuildFilterStrings(SoundFileClass
  257. : TGLSoundFileClass; out Descriptions, Filters: string);
  258. var
  259. c, i: Integer;
  260. p: PSoundFileFormat;
  261. begin
  262. Descriptions := '';
  263. Filters := '';
  264. c := 0;
  265. for i := Count - 1 downto 0 do
  266. begin
  267. p := PSoundFileFormat(Items[i]);
  268. if p^.SoundFileClass.InheritsFrom(SoundFileClass) and (p^.Extension <> '')
  269. then
  270. with p^ do
  271. begin
  272. if c <> 0 then
  273. begin
  274. Descriptions := Descriptions + '|';
  275. Filters := Filters + ';';
  276. end;
  277. if (Description = '') and (DescResID <> 0) then
  278. Description := LoadStr(DescResID);
  279. FmtStr(Descriptions, '%s%s (*.%s)|*.%2:s', [Descriptions, Description,
  280. Extension]);
  281. FmtStr(Filters, '%s*.%s', [Filters, Extension]);
  282. Inc(c);
  283. end;
  284. end;
  285. if c > 1 then
  286. FmtStr(Descriptions, '%s (%s)|%1:s|%s',
  287. [sAllFilter, Filters, Descriptions]);
  288. end;
  289. // ------------------------------------------------------------------
  290. initialization
  291. // ------------------------------------------------------------------
  292. finalization
  293. FreeAndNil(vSoundFileFormats);
  294. end.