1 /* soundfile.c - low-level sound file I/O implementation
2  *
3  * Based on libsndfile (http://www.mega-nerd.com/libsndfile/).
4  *
5  * Copyright 2010 Petteri Hintsanen <petterih@iki.fi>
6  *
7  * This file is part of abx.
8  *
9  * abx is free software: you can redistribute it and/or modify it
10  * under the terms of the GNU General Public License as published by
11  * the Free Software Foundation, either version 3 of the License, or
12  * (at your option) any later version.
13  *
14  * abx is distributed in the hope that it will be useful, but WITHOUT
15  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
16  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
17  * License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with abx.  If not, see <http://www.gnu.org/licenses/>.
21  */
22 
23 #include "soundfile.h"
24 #include <assert.h>
25 #include <glib.h>
26 #include <sndfile.h>
27 
28 /*
29  * Sound file handle type.  This struct should not be accessed
30  * directly from user code.  See open_sound_file.
31  */
32 struct Sound_file {
33     SNDFILE* sffile;            /* Initialized sndfile handle. */
34     Metadata metadata;
35 };
36 
37 /*
38  * Open sound file for reading.  Return handle to the opened sound
39  * file, or NULL on error.
40  */
41 Sound_file *
open_sound_file(const char * filename)42 open_sound_file(const char *filename)
43 {
44     Sound_file* sndfile;
45     SNDFILE *sffile;
46     SF_INFO sfinfo;
47 
48     if (!filename) return NULL;
49 
50     sfinfo.format = 0;
51     sffile = sf_open(filename, SFM_READ, &sfinfo);
52     if (!sffile) return NULL;
53 
54     sndfile = g_malloc(sizeof(Sound_file));
55     sndfile->sffile = sffile;
56 
57     if (sfinfo.format & SF_FORMAT_PCM_S8) sndfile->metadata.bits = 8;
58     else if (sfinfo.format & SF_FORMAT_PCM_16) sndfile->metadata.bits = 16;
59     else if (sfinfo.format & SF_FORMAT_PCM_24) sndfile->metadata.bits = 24;
60     else if (sfinfo.format & SF_FORMAT_PCM_32) sndfile->metadata.bits = 32;
61     else {
62         g_error("unsupported bits per sample "
63                 "(must be 8, 16, 24, or 32)");
64     }
65 
66     sndfile->metadata.filename = g_strdup(filename);
67     sndfile->metadata.rate = sfinfo.samplerate;
68     sndfile->metadata.channels = sfinfo.channels;
69     sndfile->metadata.frames = sfinfo.frames;
70     sndfile->metadata.duration = (1.0 * sndfile->metadata.frames
71                                   / sndfile->metadata.rate);
72     sndfile->metadata.minutes =
73 	(int) sndfile->metadata.duration / 60;
74     sndfile->metadata.seconds =
75 	(int) sndfile->metadata.duration % 60;
76 
77     /* g_debug("opened '%s':\n" */
78     /*         "  frames         : %i\n" */
79     /*         "  sampling rate  : %i\n" */
80     /*         "  bits per sample: %i\n" */
81     /*         "  channels       : %i\n" */
82     /*         "  duration       : %f (%d:%d)",  */
83     /*         filename, sndfile->metadata.frames,  */
84     /*         sndfile->metadata.rate, sndfile->metadata.bits,  */
85     /*         sndfile->metadata.channels, sndfile->metadata.duration,  */
86     /*         sndfile->metadata.minutes, sndfile->metadata.seconds); */
87 
88     return sndfile;
89 }
90 
91 /*
92  * Close sound file.  Return 0 on success, or non-zero on error.
93  */
94 int
close_sound_file(Sound_file * sndfile)95 close_sound_file(Sound_file *sndfile)
96 {
97     int rval;
98     assert(sndfile);
99     rval = sf_close(sndfile->sffile);
100     g_free(sndfile->metadata.filename);
101     g_free(sndfile);
102     return rval;
103 }
104 
105 /*
106  * Return sound file metadata.
107  */
108 Metadata
get_metadata(Sound_file * sndfile)109 get_metadata(Sound_file *sndfile)
110 {
111     assert(sndfile);
112     return sndfile->metadata;
113 }
114 
115 /*
116  * Seek sound file.  Offset (in seconds) and whence are as in fseek.
117  * Return the new location, or -1 on error.
118  */
119 double
seek_sound_file(Sound_file * sndfile,double offset,int whence)120 seek_sound_file(Sound_file *sndfile, double offset, int whence)
121 {
122     sf_count_t frames, loc;
123     assert(sndfile);
124     /* convert offset to frame count */
125     frames = sndfile->metadata.rate * offset;
126     loc = sf_seek(sndfile->sffile, frames, whence);
127     if (loc != -1) loc = 1.0 * loc / sndfile->metadata.rate;
128     return loc;
129 }
130 
131 /*
132  * Read nframes frames of normalized (32-bit floating point) PCM data
133  * from sound file to buf.  Return the number of frames read.
134  */
135 unsigned int
read_pcm_data(Sound_file * sndfile,float * buf,unsigned int nframes)136 read_pcm_data(Sound_file *sndfile, float *buf, unsigned int nframes)
137 {
138     assert(sndfile && buf && nframes >= 0);
139     return sf_readf_float(sndfile->sffile, buf, nframes);
140 }
141