1 // This file is part of Dust Racing 2D.
2 // Copyright (C) 2013 Jussi Lind <jussi.lind@iki.fi>
3 //
4 // Dust Racing 2D is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 // Dust Racing 2D is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 // GNU General Public License for more details.
12 //
13 // You should have received a copy of the GNU General Public License
14 // along with Dust Racing 2D. If not, see <http://www.gnu.org/licenses/>.
15 
16 #include "openaloggdata.hpp"
17 
18 #include <AL/alc.h>
19 
20 #include <cstdio>
21 #include <vector>
22 #include <stdexcept>
23 
24 #include <vorbis/vorbisfile.h>
25 
checkError()26 static bool checkError()
27 {
28     ALCenum error = alGetError();
29     return error == AL_NO_ERROR;
30 }
31 
32 static const int BUFFER_SIZE = 32768; // 32 KB buffers
33 
34 // This routine is originally from
35 // http://www.gamedev.net/page/resources/_/technical/game-programming/introduction-to-ogg-vorbis-r2031
loadOgg(const char * fileName,std::vector<char> & buffer,ALenum & format,ALsizei & freq)36 static void loadOgg(const char * fileName, std::vector<char> & buffer, ALenum & format, ALsizei & freq)
37 {
38     int endian = 0; // 0 for Little-Endian, 1 for Big-Endian
39     int bitStream;
40     long bytes;
41     char array[BUFFER_SIZE]; // Local fixed size array
42     FILE * f;
43 
44     // Open for binary reading
45     f = std::fopen(fileName, "rb");
46 
47     vorbis_info * pInfo;
48     OggVorbis_File oggFile;
49     ov_open(f, &oggFile, NULL, 0);
50 
51     // Get some information about the OGG file
52     pInfo = ov_info(&oggFile, -1);
53 
54     // Check the number of channels... always use 16-bit samples
55     if (pInfo->channels == 1)
56     {
57         format = AL_FORMAT_MONO16;
58     }
59     else
60     {
61         format = AL_FORMAT_STEREO16;
62     }
63 
64     freq = pInfo->rate;
65 
66     do
67     {
68         // Read up to a buffer's worth of decoded sound data
69         bytes = ov_read(&oggFile, array, BUFFER_SIZE, endian, 2, 1, &bitStream);
70 
71         // Append to end of buffer
72         buffer.insert(buffer.end(), array, array + bytes);
73 
74     } while (bytes > 0);
75 
76     ov_clear(&oggFile);
77 }
78 
OpenALOggData(const std::string & path)79 OpenALOggData::OpenALOggData(const std::string & path)
80   : m_freq(0)
81   , m_buffer(0)
82 {
83     load(path);
84 }
85 
load(const std::string & path)86 void OpenALOggData::load(const std::string & path)
87 {
88     Data::load(path);
89 
90     alGetError();
91 
92     if (!m_buffer)
93     {
94         alGenBuffers(1, &m_buffer);
95     }
96 
97     std::vector<char> oggBuffer;
98     loadOgg(path.c_str(), oggBuffer, m_format, m_freq);
99 
100     alBufferData(m_buffer, m_format, &oggBuffer[0], static_cast<ALsizei>(oggBuffer.size()), m_freq);
101 
102     if (!checkError())
103     {
104         throw std::runtime_error("Failed to set buffer data of '" + path + "'");
105     }
106 }
107 
buffer() const108 ALuint OpenALOggData::buffer() const
109 {
110     return m_buffer;
111 }
112 
~OpenALOggData()113 OpenALOggData::~OpenALOggData()
114 {
115     alDeleteBuffers(1, &m_buffer);
116 }
117