1 /* B.Harvestr
2  * LV2 Plugin
3  *
4  * Copyright (C) 2018 by Sven Jähnichen
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3, or (at your option)
9  * any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19  */
20 
21 #ifndef SAMPLE_HPP_
22 #define SAMPLE_HPP_
23 
24 #include "sndfile.h"
25 #include <cstdlib>
26 #include <cstring>
27 #include <string>
28 #include <stdexcept>
29 
30 #ifndef SF_FORMAT_MP3
31 #define MINIMP3_IMPLEMENTATION
32 #define MINIMP3_FLOAT_OUTPUT
33 #include "minimp3_ex.h"
34 #endif /* SF_FORMAT_MP3 */
35 
36 
37 struct Sample
38 {
39         SF_INFO  info;      // Info about sample from sndfile
40         float*   data;      // Sample data in float
41         char*    path;      // Path of file
42 
SampleSample43         Sample () : info {0, 0, 0, 0, 0, 0}, data (nullptr), path (nullptr) {}
44 
SampleSample45         Sample (const char* samplepath) : info {0, 0, 0, 0, 0, 0}, data (nullptr), path (nullptr)
46         {
47                 if (!samplepath) return;
48 
49         	int len = strlen (samplepath);
50                 path = (char*) malloc (len + 1);
51                 if (!path) throw std::bad_alloc();
52                 memcpy (path, samplepath, len + 1);
53 
54                 // Extract file extension
55                 char* extptr = strrchr (path, '.');
56                 const int extsz = (extptr ? strlen (extptr) + 1 : 1);
57                 char* ext = (char*) malloc (extsz);
58                 if (!ext) throw std::bad_alloc();
59                 ext[0] = 0;
60                 if (extsz > 1) memcpy (ext, extptr, extsz);
61                 for (char* s = ext; *s; ++s) *s = tolower ((unsigned char)*s);
62 
63 
64                 // Check for known non-sndfiles
65 #ifdef MINIMP3_IMPLEMENTATION
66                 if (!strcmp (ext, ".mp3"))
67                 {
68                         mp3dec_t mp3dec;
69                         mp3dec_file_info_t mp3info;
70                         if (mp3dec_load (&mp3dec, path, &mp3info, NULL, NULL)) throw std::invalid_argument ("Can't open " + std::string (path) + ".");
71 
72                         info.samplerate = mp3info.hz;
73                         info.channels = mp3info.channels;
74                         info.frames = mp3info.samples / mp3info.channels;
75 
76                         data = (float*) malloc (sizeof(float) * info.frames * info.channels);
77                         if (!data) throw std::bad_alloc();
78 
79                         memcpy (data, mp3info.buffer, sizeof(float) * info.frames * info.channels);
80                 }
81 
82                 else
83 #endif /* MINIMP3_IMPLEMENTATION */
84 
85         	{
86                         SNDFILE* sndfile = sf_open (samplepath, SFM_READ, &info);
87 
88                         if (!sndfile || !info.frames) throw std::invalid_argument ("Can't open " + std::string (path) + ".");
89 
90                         // Read & render data
91                         data = (float*) malloc (sizeof(float) * info.frames * info.channels);
92                         if (!data)
93                 	{
94                 		sf_close (sndfile);
95                 		throw std::bad_alloc();
96                 	}
97 
98                         sf_seek (sndfile, 0, SEEK_SET);
99                         sf_read_float (sndfile, data, info.frames * info.channels);
100                         sf_close (sndfile);
101                 }
102         }
103 
~SampleSample104         ~Sample()
105         {
106         	if (data) free (data);
107         	if (path) free (path);
108         }
109 
getSample110         float get (const sf_count_t frame, const int channel, const int rate)
111         {
112         	if (!data) return 0.0f;
113 
114         	// Direct access if same frame rate
115         	if (info.samplerate == rate)
116         	{
117         		if (frame >= info.frames) return 0.0f;
118         		else return data[frame * info.channels + channel];
119         	}
120 
121         	// Linear rendering (TODO) if frame rates differ
122         	double f = (frame * info.samplerate) / rate;
123         	sf_count_t f1 = f;
124 
125         	if (f1 + 1 >= info.frames) return 0.0f;
126         	if (f1 == f) return data[f1 * info.channels + channel];
127 
128         	float data1 = data[f1 * info.channels + channel];
129         	float data2 = data[(f1 + 1) * info.channels + channel];
130         	return data1 + (f - double (f1)) * (data2 - data1);
131         }
132 };
133 
134 #endif /* SAMPLE_HPP_ */
135