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