4039dabc0e3837679c06ea209e64eec876a21d4e
[camargo/neiasound.git] / src / sndfile / nSndfileStream.cpp
1 // Copyright (C) 2015 Lucas Pires Camargo
2 // 
3 // This file is part of neiasound - Qt-style OpenAL wrapper for games.
4 // 
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions are
7 // met:
8 // 
9 // 1. Redistributions of source code must retain the above copyright notice,
10 // this list of conditions and the following disclaimer.
11 // 
12 // 2. Redistributions in binary form must reproduce the above copyright notice,
13 // this list of conditions and the following disclaimer in the documentation
14 // and/or other materials provided with the distribution.
15 // 
16 // THIS SOFTWARE IS PROVIDED BY THE FREEBSD PROJECT ``AS IS'' AND ANY EXPRESS
17 // OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 // OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
19 // NO EVENT SHALL THE FREEBSD PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
20 // INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 // Copyright (C) 2015 Lucas Pires Camargo
27 // 
28 // This file is part of neiasound - Qt-style OpenAL wrapper for games.
29 // 
30 // Redistribution and use in source and binary forms, with or without
31 // modification, are permitted provided that the following conditions are
32 // met:
33 // 
34 // 1. Redistributions of source code must retain the above copyright notice,
35 // this list of conditions and the following disclaimer.
36 // 
37 // 2. Redistributions in binary form must reproduce the above copyright notice,
38 // this list of conditions and the following disclaimer in the documentation
39 // and/or other materials provided with the distribution.
40 // 
41 // THIS SOFTWARE IS PROVIDED BY THE FREEBSD PROJECT ``AS IS'' AND ANY EXPRESS
42 // OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
43 // OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
44 // NO EVENT SHALL THE FREEBSD PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
45 // INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
46 // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
47 // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
48 // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
49 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
50 // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
51 #include "nSndfileStream.h"
52 #include "sndfile.h"
53 #include <cstdio>
54 #include <QDataStream>
55 #include "../nSoundBag.h"
56
57 // SF_VIRTUAL_IO handler functions
58
59 sf_count_t nSndfileStream_vio_filelen(void * userData)
60 {
61     QIODevice * device = ((QIODevice*)userData);
62     sf_count_t size = device->size();
63     return size;
64 }
65
66 sf_count_t nSndfileStream_vio_seek(sf_count_t offset, int whence, void * userData)
67 {
68     QIODevice * device = ((QIODevice*)userData);
69     switch(whence)
70     {
71     case SEEK_SET:
72         device->seek(offset);
73         return 0;
74
75     case SEEK_CUR:
76         device->seek(device->pos()+offset);
77         return 0;
78
79     case SEEK_END:
80         device->seek(device->size()-offset);
81         return 0;
82     }
83
84     return -1;
85 }
86
87 sf_count_t nSndfileStream_vio_read(void * ptr, sf_count_t count, void * userData)
88 {
89     QIODevice * device = ((QIODevice*)userData);
90     return device->read((char*)ptr, count);
91 }
92
93 sf_count_t nSndfileStream_vio_write(const void * ptr, sf_count_t count, void * userData)
94 {
95     // WRITING UNSUPPORTED
96     return -1;
97 }
98
99 sf_count_t nSndfileStream_vio_tell(void * userData)
100 {
101     QIODevice * device = ((QIODevice*)userData);
102     sf_count_t pos = device->pos();
103     return pos;
104 }
105
106
107
108
109
110
111 // ------------------------
112 // class nSndfileStream
113 // ------------------------
114
115 nSndfileStream::nSndfileStream(QString filename, QObject * parent)
116     :nSoundStream(parent)
117 {
118     m_iodevice = 0;
119     m_ownsDevice = false;
120     m_virtualio = 0;
121     m_sndinfo = new SF_INFO();
122     ((SF_INFO*)m_sndinfo)->format = 0;
123
124     m_sndfile = sf_open(filename.toLocal8Bit(), SFM_READ, (SF_INFO*)m_sndinfo);
125     if(!m_sndfile)
126         throw QString("nSndfileStream::nSndfileStream(QString): Failed to open file: ")+filename;
127
128     fillInfo();
129 }
130
131 nSndfileStream::nSndfileStream(QIODevice * device, QObject * parent, bool ownsDevice)
132     :nSoundStream(parent), m_ownsDevice(ownsDevice)
133 {
134     m_iodevice = device;
135     if(!m_iodevice->isOpen())
136         if(!m_iodevice->open(QIODevice::ReadOnly))
137             throw QString("nSndfileStream::nSndfileStream(QIODevice*): Failed to open device for reading.");
138
139     m_sndinfo = new SF_INFO();
140     ((SF_INFO*)m_sndinfo)->format = 0;
141     m_virtualio = new SF_VIRTUAL_IO();
142
143     //setup function pointers
144     SF_VIRTUAL_IO & vio = *((SF_VIRTUAL_IO*)m_virtualio);
145     vio.get_filelen = nSndfileStream_vio_filelen;
146     vio.read = nSndfileStream_vio_read;
147     vio.write = nSndfileStream_vio_write;
148     vio.seek = nSndfileStream_vio_seek;
149     vio.tell = nSndfileStream_vio_tell;
150
151     m_sndfile = sf_open_virtual((SF_VIRTUAL_IO*)m_virtualio, SFM_READ, (SF_INFO*)m_sndinfo, m_iodevice);
152
153     if(!m_sndfile)
154         throw QString("nSndfileStream::nSndfileStream(OgreStreamEtc...): Failed to open virtual stream.");
155
156     fillInfo();
157 }
158
159 nSndfileStream::~nSndfileStream()
160 {
161     if(m_virtualio) delete ((SF_VIRTUAL_IO*)m_virtualio);
162
163     sf_close((SNDFILE*)m_sndfile);
164     delete m_sndinfo;
165
166     if(m_ownsDevice && m_iodevice)
167         delete m_iodevice;
168 }
169
170 nSoundBag * nSndfileStream::createSoundBag(QObject * parent)
171 {
172     nSoundBag * bag = new nSoundBag(format(), m_info_frames, m_info_samplerate, parent);
173     read(bag->m_data, m_info_frames);
174     return bag;
175 }
176
177 void nSndfileStream::fillInfo()
178 {
179     m_info_frames = (((SF_INFO*)(m_sndinfo)))->frames;
180     m_info_format = (((SF_INFO*)(m_sndinfo)))->format;
181     m_info_samplerate = (((SF_INFO*)(m_sndinfo)))->samplerate;
182     m_info_channels = (((SF_INFO*)(m_sndinfo)))->channels;
183     // nLog::defaultLog(QString("nSndFileStream: Created new stream, %1 frames, %2hz, %3s, %4 channels.").arg(m_info_frames).arg(m_info_samplerate).arg(((double)m_info_frames)/m_info_samplerate).arg(m_info_channels), nLog::LL_WHOGIVESAFUCKANYWAY);
184
185 }
186
187 nSoundFormat nSndfileStream::format()
188 {
189     if(m_info_channels==1)
190         return SF_16BIT_MONO;
191
192     if(m_info_channels==2)
193         return SF_16BIT_STEREO;
194
195     return SF_UNDEFINED;
196 }
197
198 bool nSndfileStream::suggestStreaming()
199 {
200     if( ((m_info_format & SF_FORMAT_VORBIS) != 0) || (m_info_frames > m_info_samplerate*5))
201         return true; //suggest stream if ogg vorbis or if larger than 5 seconds
202 }
203
204
205 void nSndfileStream::rewind()
206 {
207     sf_seek((SNDFILE*)m_sndfile, 0, SEEK_SET);
208 }
209
210 quint64 nSndfileStream::read(void* data, unsigned long frames)
211 {
212     return sf_readf_short((SNDFILE*)m_sndfile, (short*)data, frames);
213 }