1 module evael.audio.Sound;
2 
3 import derelict.sndfile.sndfile;
4 
5 import evael.audio.AL;
6 import evael.audio.Source;
7 import evael.audio.AudioException;
8 
9 import evael.system.Asset;
10 
11 import evael.utils.Config;
12 
13 import dnogc.DynamicArray;
14 
15 /**
16  * Sound asset.
17  */
18 class Sound : IAsset
19 {
20 	private uint m_buffer;
21 	
22 	/// List of sources for this sound.
23     private DynamicArray!uint m_sources;
24     
25 	/**
26 	 * Sound constructor.
27 	 * Params:
28 	 *		buffer: AL buffer id
29 	 */
30 	@nogc
31 	public this(in uint buffer) nothrow
32 	{
33 		this.m_buffer = buffer;
34 	}
35 
36 	/**
37 	 * Sound destructor.
38 	 */
39  	@nogc
40 	public void dispose() nothrow
41 	{
42         foreach (id; this.m_sources)
43         {
44             al.DeleteSources(1, &id);
45         }
46 
47 		al.DeleteBuffers(1, &this.m_buffer);
48 	}
49 
50 	/**
51 	 * Creates a new source linked to this sound resource.
52 	 */
53 	@nogc
54     public Source createSource() nothrow
55     {
56         uint id;
57 		al.GenSources(1, &id);
58 
59         this.m_sources.insert(id);
60 
61         return Source(id, this.m_buffer);
62     }
63     
64 	/**
65 	 * Loads sound.
66 	 * Params:
67 	 *		soundName: sound to load
68 	 */
69 	public static Sound load(in string soundName)
70     {
71         import std.string : format, toStringz;
72         
73 		auto fileName = toStringz(Config.Paths.sounds!string ~ soundName);
74 
75 		SF_INFO fileInfo;
76 
77 		auto file = sf_open(fileName, SFM_READ, &fileInfo);
78 
79 		if (!file)
80 		{
81 			throw new AudioFileNotFoundException(format("Can't open file %s", soundName));
82 		}
83 
84 		short[] samples = new short[cast(uint)fileInfo.frames];
85 
86 		if (sf_read_short(file, samples.ptr, fileInfo.frames) != fileInfo.frames)
87 		{
88 			throw new Exception("Something is wrong");
89 		}
90 
91 		auto fileFormat = getFormatFromChannelCount(fileInfo.channels);
92 
93 		if (fileFormat == 0)
94 		{
95 			throw new AudioUnsupportedChannelsException(format("Can't open file %s, unsupported number of channels %d", soundName, fileInfo.channels));
96 		}
97 
98 		uint buffer;
99 
100 		// Create the buffer
101 		al.GenBuffers(1, &buffer);
102 		al.BufferData(buffer, fileFormat, samples.ptr, ushort.sizeof * samples.length, fileInfo.samplerate);
103 
104         return new Sound(buffer);
105     }
106 
107     /**
108      * Finds the sound format according to the number of channels.
109      * Params:
110      * 		channelCount: number of channels
111      */
112 	@nogc
113     public static int getFormatFromChannelCount(in uint channelCount) nothrow
114     {
115         int format;
116 
117         switch (channelCount)
118         {
119             case 1:  format = AL_FORMAT_MONO16;                    break;
120             case 2:  format = AL_FORMAT_STEREO16;                  break;
121             case 4:  format = alGetEnumValue("AL_FORMAT_QUAD16");  break;
122             case 6:  format = alGetEnumValue("AL_FORMAT_51CHN16"); break;
123             case 7:  format = alGetEnumValue("AL_FORMAT_61CHN16"); break;
124             case 8:  format = alGetEnumValue("AL_FORMAT_71CHN16"); break;
125             default: format = 0;                                   break;
126         }
127 
128         return format;
129     }
130 
131 }