├── theora2flv
├── Makefile
├── audio.pyx
├── arecord.py
├── amplify.py
├── README
├── record.py
└── arecord.c
/theora2flv:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | ffmpeg -b 800k -i $1 $1.flv
4 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | all:
2 | cython audio.pyx
3 | gcc -fPIC -I/usr/include/python2.6 -c -o audio.o audio.c
4 | gcc -fPIC -c -o arecord.o arecord.c
5 | gcc -shared -o audio.so audio.o arecord.o -lasound
6 |
--------------------------------------------------------------------------------
/audio.pyx:
--------------------------------------------------------------------------------
1 | cdef extern int run(char *filename) nogil
2 | cdef extern void stop() nogil
3 |
4 | def capture(filename):
5 | with nogil:
6 | run(filename)
7 |
8 | def capture_stop():
9 | with nogil:
10 | stop()
11 |
--------------------------------------------------------------------------------
/arecord.py:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env python
2 |
3 | from time import sleep
4 | from threading import Thread
5 |
6 | from audio import capture, capture_stop
7 |
8 | class Audio(Thread):
9 |
10 | def __init__(self, filename):
11 | Thread.__init__(self)
12 | self._filename = filename
13 |
14 | def run(self):
15 | capture(self._filename)
16 |
17 | def stop(self):
18 | capture_stop()
19 |
20 | a = Audio("c.wav")
21 | a.start()
22 | try:
23 | while 1:
24 | sleep(0.1)
25 | finally:
26 | a.stop()
27 |
--------------------------------------------------------------------------------
/amplify.py:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env python
2 |
3 | """
4 | Amplifies a wav file.
5 | """
6 |
7 | from sys import argv
8 | import wave
9 | import audioop
10 |
11 | def normalize(filein, fileout):
12 | a = wave.open(filein)
13 | params = a.getparams()
14 | nchannels, sampwidth, framerate, nframes, comptype, compname = params
15 | data = a.readframes(nframes)
16 |
17 | if sampwidth == 1:
18 | max_val = 0x7f
19 | elif sampwidth == 2:
20 | max_val = 0x7fff
21 | elif sampwidth == 4:
22 | max_val == 0x7fffffff
23 |
24 | max = audioop.max(data, sampwidth)
25 | factor = float(max_val)/max
26 | #factor = 200
27 | print "amplifying by F=%f" % factor
28 |
29 | data = audioop.mul(data, sampwidth, factor)
30 |
31 | b = wave.open(fileout, "w")
32 | b.setparams(params)
33 | b.writeframes(data)
34 |
35 | if len(argv) == 3:
36 | normalize(argv[1], argv[2])
37 | else:
38 | print "usage: amplify.py infile outfile"
39 |
--------------------------------------------------------------------------------
/README:
--------------------------------------------------------------------------------
1 | Record
2 | ------
3 |
4 | The record.py script uses gtk's "get_from_drawable()" function to take
5 | screenshots and modified alsa's arecord to record the sound.
6 |
7 | More details how it works:
8 |
9 | The arecord and aplay alsa utilities are actually one and the same program, so
10 | it was stripped down, all signals removed, all playback removed, all header
11 | files merged into it, then a "capture_stop" global variable was added, which if
12 | set to 1, the main audio capture loop will end. Then it is called from cython
13 | using nogil in it's own thread and when the user wants to end it, the
14 | "capture_stop" is set to 1. The main python thread takes screenshots in
15 | periodic intervals (15 fps by default) and if it's late, it skips the frame, so
16 | that the next one is on time. All screenshots are saved to the "data" file in
17 | the temporary directory, which is later read and converted to a set of png
18 | images. The audio is saved to a wav file.
19 |
20 | It is then your job to create a video from it (and to convert sound to any
21 | format you like, like ogg vorbis), it tells you some suggestions (see below).
22 | Also the amplifier.py script is provided to amplify volume of the wav file in
23 | case it is too quiet.
24 |
25 | Usage
26 | -----
27 |
28 | Install the following packages in Debian/Ubuntu:
29 |
30 | sudo apt-get install python-gtk2 libasound2-dev
31 |
32 | and run it:
33 |
34 | ./record.py
35 |
36 | It produces a wav file in the tmp directory (it tells you which) and a set of
37 | png images. It also suggests you exact commands how to create videos from it,
38 | using either mencoder or ffmpeg2theora (it's up to you).
39 |
40 | In my experience mencoder can't sync the sound and video correctly, while
41 | ffmpeg2theora just works. If the sound is too quiet, use the amplifier.py
42 | utility to amplify the wav file before converting it to ogg or the
43 | "normalize-audio" program (better results) from the Debian package of the same
44 | name (it operates *inplace* on the wav file).
45 |
46 | Convert to FLV
47 | --------------
48 |
49 | In order to convert to FLV, so that you can play it with flowplayer (flash
50 | player in the browser), use the script theora2flv.
51 |
52 | License
53 | -------
54 |
55 | The arecord.c file is GPL (see alsa-utils for more info), all the other code is
56 | BSD. The package as a whole is GPL due to arecord.c.
57 |
58 | Flowplayer
59 | ----------
60 |
61 | Example of usage:
62 |
63 |
65 |
67 |
77 |
--------------------------------------------------------------------------------
/record.py:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env python
2 |
3 | """
4 | Captures a video of a window plus audio, then encodes this into one media file.
5 |
6 | Other tips:
7 |
8 | 1) extracting audio from avi file:
9 |
10 | $ mplayer -ao pcm:fast:file=audio_out.wav -vo null -vc null some_video.ogv
11 |
12 | 2) taking video from one file and audio from another:
13 |
14 | $ mencoder -audiofile audio_out.wav -oac lavc -ovc lavc some_video.ogv \
15 | -o out.avi
16 |
17 | """
18 |
19 | import sys
20 | import os
21 | import re
22 | from time import sleep
23 | from timeit import default_timer as clock
24 | from subprocess import Popen, PIPE, check_call, STDOUT
25 | from tempfile import mkdtemp
26 | from select import select
27 | from cPickle import dump, load
28 | from optparse import OptionParser
29 | from threading import Thread
30 |
31 | import gtk
32 | from PIL import Image
33 |
34 | from audio import capture, capture_stop
35 |
36 | class Audio(Thread):
37 |
38 | def __init__(self, filename):
39 | Thread.__init__(self)
40 | self._filename = filename
41 |
42 | def run(self):
43 | capture(self._filename)
44 |
45 | def stop(self):
46 | capture_stop()
47 |
48 | class Video(object):
49 |
50 | def __init__(self, tmpdir, win_id=None, fps=15):
51 | """
52 | Starts capturing the video and saves it to a file 'filename'.
53 |
54 | win_id ... the window id to capture, if None, it automatically runs
55 | xwininfo and parses the output to capture the windows id
56 | """
57 | x, y, w, h = self.get_active_window_pos()
58 | self.x = x
59 | self.y = y
60 | self.width = w
61 | self.height = h
62 | self.fps = fps
63 | self.tmpdir = tmpdir
64 | self.screengrab = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, False, 8, w, h)
65 | self._datafile = open(tmpdir+"/data", "w")
66 | headers = (w, h, self.screengrab.get_rowstride(), fps)
67 | dump(headers, self._datafile)
68 |
69 | def wait(self, fps=2):
70 | """
71 | Generates consecutive integers with the given "fps".
72 |
73 | It maintains the constant fps, and if necessary, it skips frames (in this
74 | case the "skip" variable returns the number of skipped frames).
75 | """
76 | i = 1
77 | t = clock()
78 | while 1:
79 | free_count = 0
80 | skip = 0
81 | while free_count == 0:
82 | while clock()-t < float(i)/fps:
83 | free_count += 1
84 | if free_count == 0:
85 | i += 1
86 | skip += 1
87 | yield i-1, skip
88 | i += 1
89 |
90 | def start(self):
91 | t = clock()
92 | t_start = t
93 | for i, skip in self.wait(fps=self.fps):
94 | t_new = clock()
95 | self.screengrab.get_from_drawable(gtk.gdk.get_default_root_window(),
96 | gtk.gdk.colormap_get_system(),
97 | self.x, self.y, 0, 0, self.width, self.height)
98 | img = self.screengrab.get_pixels()
99 | frame_header = (len(img), skip)
100 | dump(frame_header, self._datafile)
101 | self._datafile.write(img)
102 | i += 1
103 | print "time: %.3f, frame: %04d, current fps: %6.3f, skip: %d, " \
104 | "lag: %.6f" % ( t_new-t_start, i, 1/(t_new-t), skip,
105 | t_new-t_start - float(i)/self.fps)
106 | t = t_new
107 |
108 | def get_window_pos(self, win_id, dX=0, dY=-17, dw=2, dh=16):
109 | """
110 | Determines the correct window position and dimensions.
111 |
112 | win_id ... windows ID to whose parameters are to be determined
113 | dX, dY, dw, dh ... those are corrections that have to be applied to
114 | what is read from xwininfo
115 |
116 | Note: they are not precised anyways, there seems to be some weird
117 | rounding to multiplies of 4...
118 | """
119 | p = Popen(["xwininfo", "-id", win_id], stdout=PIPE)
120 | out = p.communicate()[0]
121 | if p.returncode != 0:
122 | raise Exception("xwininfo failed")
123 | X = int(re.search("Absolute upper-left X:.*?(\d+)", out).groups()[0])
124 | Y = int(re.search("Absolute upper-left Y:.*?(\d+)", out).groups()[0])
125 | width = int(re.search("Width:.*?(\d+)", out).groups()[0])
126 | height = int(re.search("Height:.*?(\d+)", out).groups()[0])
127 | X += dX
128 | Y += dY
129 | width += dw
130 | height += dh
131 | return X, Y, width, height
132 |
133 | def get_active_window_pos(self):
134 | root = gtk.gdk.screen_get_default()
135 | win = root.get_active_window()
136 | winw, winh = win.get_geometry()[2:4]
137 | _or, _ror = win.get_origin(), win.get_root_origin()
138 | border, titlebar = _or[0] - _ror[0], _or[1] - _ror[1]
139 | w, h = winw + (border*2), winh + (titlebar+border)
140 | x, y = win.get_root_origin()
141 | return x, y, w, h
142 |
143 | def get_window_id(self):
144 | p = Popen("xwininfo", stdout=PIPE)
145 | out = p.communicate()[0]
146 | if p.returncode != 0:
147 | raise Exception("xwininfo failed")
148 | s = "Window id: "
149 | i1 = out.find(s) + len(s)
150 | i2 = out.find(" ", i1)
151 | id = out[i1: i2]
152 | return id
153 |
154 | def convert(self):
155 | self._datafile.close()
156 | f = open(self.tmpdir+"/data")
157 | data = load(f)
158 | img_width, img_height, stride, fps = data
159 | print img_width, img_height, stride, fps
160 | i = 0
161 | while 1:
162 | try:
163 | frame_headers = load(f)
164 | except EOFError:
165 | break
166 | n, skip = frame_headers
167 | for j in range(skip):
168 | # ideally this should be interpolated with the next image
169 | img.save(self.tmpdir + "/screen%04d.png" % i)
170 | i += 1
171 | pixels = f.read(n)
172 | img = Image.frombuffer("RGB", (img_width, img_height),
173 | pixels, "raw", "RGB", stride, 1)
174 | img.save(self.tmpdir + "/screen%04d.png" % i)
175 | print i
176 | i += 1
177 | print "images saved to: %s" % self.tmpdir
178 |
179 |
180 | def encode(audio, video, output):
181 | """
182 | Combines the audio and video to a resulting file.
183 | """
184 | check_call(["mencoder", "-audiofile", audio, "-oac", "lavc", "-ovc",
185 | "lavc", video, "-o", output], stdin=PIPE, stdout=PIPE, stderr=STDOUT)
186 |
187 | if __name__ == "__main__":
188 | parser = OptionParser()
189 | parser.add_option("-f", "--file", dest="filename", default="out.avi",
190 | help="save to FILE [default: %default]", metavar="FILE")
191 | parser.add_option("-w", "--window", dest="window",
192 | help="window id to capture", default=None)
193 | options, args = parser.parse_args()
194 |
195 | tmp_dir = mkdtemp()
196 | video_file = os.path.join(tmp_dir, "video.ogv")
197 | audio_file = os.path.join(tmp_dir, "audio.wav")
198 | print "work dir:", tmp_dir
199 | print "select a window to capture (2s sleep)"
200 | sleep(2)
201 | print "active window selected"
202 | v = Video(tmp_dir, options.window)
203 | a = Audio(audio_file)
204 | print "Capturing audio and video. Press CTRL-C to stop."
205 | try:
206 | try:
207 | a.start()
208 | v.start()
209 | except KeyboardInterrupt:
210 | pass
211 | finally:
212 | a.stop()
213 | print "stopped."
214 | print "converting to png images"
215 | v.convert()
216 | print "To encode using mencoder:"
217 | print "-"*80
218 | print "mencoder mf://%s/*.png -mf fps=%d -audiofile %s -oac lavc " \
219 | "-ovc lavc -lavcopts vcodec=mpeg4:vbitrate=800 -o v.avi" % \
220 | (tmp_dir, v.fps, audio_file)
221 | print "-"*80
222 | print
223 | print "To encode using theora:"
224 | print "-"*80
225 | print "ffmpeg2theora -F %d -v 10 %s/screen%%04d.png -o tmp.ogv" % \
226 | (v.fps, tmp_dir)
227 | print "normalize-audio %s # beware: normalizes *in place*" % audio_file
228 | print "oggenc %s" % audio_file
229 | print "oggz-merge -o v.ogv tmp.ogv %sogg" % audio_file[:-3]
230 | print "-"*80
231 | print
232 | print "To just archive audio and video in a lossless format for later processing:"
233 | print "-"*80
234 | print "advmng -v -a %d video.mng %s/screen*.png" % (v.fps, tmp_dir)
235 | print "flac -o audio.flac %s" % audio_file
236 | print "-"*80
237 | print "(Use 'advmng -x video.mng' to extract the png images)"
238 |
--------------------------------------------------------------------------------
/arecord.c:
--------------------------------------------------------------------------------
1 | /*
2 | This file was taken from alsa-utils aplay.c and stripped down, see README
3 | for more details. This file is GPL, see the license information in
4 | alsa-utils for more info.
5 | */
6 | #define _GNU_SOURCE
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 | #include
14 | #include
15 | #include
16 | #include
17 | #include
18 | #include
19 | #include
20 | #include
21 | #include
22 | #include
23 | #include
24 | #include
25 | #include
26 |
27 | #include
28 |
29 | #include
30 | #include
31 |
32 | /* Definitions for Microsoft WAVE format */
33 |
34 | #if __BYTE_ORDER == __LITTLE_ENDIAN
35 | #define COMPOSE_ID(a,b,c,d) ((a) | ((b)<<8) | ((c)<<16) | ((d)<<24))
36 | #define LE_SHORT(v) (v)
37 | #define LE_INT(v) (v)
38 | #define BE_SHORT(v) bswap_16(v)
39 | #define BE_INT(v) bswap_32(v)
40 | #elif __BYTE_ORDER == __BIG_ENDIAN
41 | #define COMPOSE_ID(a,b,c,d) ((d) | ((c)<<8) | ((b)<<16) | ((a)<<24))
42 | #define LE_SHORT(v) bswap_16(v)
43 | #define LE_INT(v) bswap_32(v)
44 | #define BE_SHORT(v) (v)
45 | #define BE_INT(v) (v)
46 | #else
47 | #error "Wrong endian"
48 | #endif
49 |
50 | #define WAV_RIFF COMPOSE_ID('R','I','F','F')
51 | #define WAV_WAVE COMPOSE_ID('W','A','V','E')
52 | #define WAV_FMT COMPOSE_ID('f','m','t',' ')
53 | #define WAV_DATA COMPOSE_ID('d','a','t','a')
54 |
55 | /* WAVE fmt block constants from Microsoft mmreg.h header */
56 | #define WAV_FMT_PCM 0x0001
57 | #define WAV_FMT_IEEE_FLOAT 0x0003
58 |
59 | /* it's in chunks like .voc and AMIGA iff, but my source say there
60 | are in only in this combination, so I combined them in one header;
61 | it works on all WAVE-file I have
62 | */
63 | typedef struct {
64 | u_int magic; /* 'RIFF' */
65 | u_int length; /* filelen */
66 | u_int type; /* 'WAVE' */
67 | } WaveHeader;
68 |
69 | typedef struct {
70 | u_short format; /* see WAV_FMT_* */
71 | u_short channels;
72 | u_int sample_fq; /* frequence of sample */
73 | u_int byte_p_sec;
74 | u_short byte_p_spl; /* samplesize; 1 or 2 bytes */
75 | u_short bit_p_spl; /* 8, 12 or 16 bit */
76 | } WaveFmtBody;
77 |
78 | typedef struct {
79 | u_int type; /* 'data' */
80 | u_int length; /* samplecount */
81 | } WaveChunkHeader;
82 |
83 | #define _(msgid) gettext (msgid)
84 | #define gettext_noop(msgid) msgid
85 | #define N_(msgid) gettext_noop (msgid)
86 |
87 | #ifndef LLONG_MAX
88 | #define LLONG_MAX 9223372036854775807LL
89 | #endif
90 |
91 | #define DEFAULT_FORMAT SND_PCM_FORMAT_U8
92 | #define DEFAULT_SPEED 8000
93 |
94 | #define FORMAT_DEFAULT -1
95 | #define FORMAT_RAW 0
96 | #define FORMAT_VOC 1
97 | #define FORMAT_WAVE 2
98 | #define FORMAT_AU 3
99 |
100 | /* global data */
101 |
102 | static snd_pcm_sframes_t (*readi_func)(snd_pcm_t *handle, void *buffer, snd_pcm_uframes_t size);
103 | static snd_pcm_sframes_t (*writei_func)(snd_pcm_t *handle, const void *buffer, snd_pcm_uframes_t size);
104 | static snd_pcm_sframes_t (*readn_func)(snd_pcm_t *handle, void **bufs, snd_pcm_uframes_t size);
105 | static snd_pcm_sframes_t (*writen_func)(snd_pcm_t *handle, void **bufs, snd_pcm_uframes_t size);
106 |
107 | enum {
108 | VUMETER_NONE,
109 | VUMETER_MONO,
110 | VUMETER_STEREO
111 | };
112 |
113 | static char *command;
114 | static snd_pcm_t *handle;
115 | static struct {
116 | snd_pcm_format_t format;
117 | unsigned int channels;
118 | unsigned int rate;
119 | } hwparams, rhwparams;
120 | static int timelimit = 0;
121 | static int quiet_mode = 0;
122 | static int file_type = FORMAT_DEFAULT;
123 | static int open_mode = 0;
124 | static int capture_stop = 0;
125 | static snd_pcm_stream_t stream = SND_PCM_STREAM_PLAYBACK;
126 | static int interleaved = 1;
127 | static int nonblock = 0;
128 | static u_char *audiobuf = NULL;
129 | static snd_pcm_uframes_t chunk_size = 0;
130 | static unsigned period_time = 0;
131 | static unsigned buffer_time = 0;
132 | static snd_pcm_uframes_t period_frames = 0;
133 | static snd_pcm_uframes_t buffer_frames = 0;
134 | static int avail_min = -1;
135 | static int start_delay = 0;
136 | static int stop_delay = 0;
137 | static int verbose = 0;
138 | static int vumeter = VUMETER_NONE;
139 | static int buffer_pos = 0;
140 | static size_t bits_per_sample, bits_per_frame;
141 | static size_t chunk_bytes;
142 | static snd_output_t *log;
143 |
144 | static int fd = -1;
145 | static off64_t pbrec_count = LLONG_MAX, fdcount;
146 | static int vocmajor, vocminor;
147 |
148 | /* needed prototypes */
149 |
150 | static void capture(char *filename);
151 |
152 | static void begin_wave(int fd, size_t count);
153 | static void end_wave(int fd);
154 |
155 | struct fmt_capture {
156 | void (*start) (int fd, size_t count);
157 | void (*end) (int fd);
158 | char *what;
159 | long long max_filesize;
160 | } fmt_rec_table[] = {
161 | { NULL, NULL, N_("raw data"), LLONG_MAX },
162 | { NULL, NULL, N_("VOC"), 16000000LL },
163 | { begin_wave, end_wave, N_("WAVE"), 2147483648LL },
164 | { NULL, NULL, N_("Sparc Audio"), LLONG_MAX }
165 | };
166 |
167 | #if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95)
168 | #define error(...) do {\
169 | fprintf(stderr, "%s: %s:%d: ", command, __FUNCTION__, __LINE__); \
170 | fprintf(stderr, __VA_ARGS__); \
171 | putc('\n', stderr); \
172 | } while (0)
173 | #else
174 | #define error(args...) do {\
175 | fprintf(stderr, "%s: %s:%d: ", command, __FUNCTION__, __LINE__); \
176 | fprintf(stderr, ##args); \
177 | putc('\n', stderr); \
178 | } while (0)
179 | #endif
180 |
181 | static void version(void)
182 | {
183 | }
184 |
185 | static void signal_handler(int sig)
186 | {
187 | if (verbose==2)
188 | putchar('\n');
189 | if (!quiet_mode)
190 | fprintf(stderr, _("Aborted by signal %s...\n"), strsignal(sig));
191 | if (stream == SND_PCM_STREAM_CAPTURE) {
192 | if (fmt_rec_table[file_type].end) {
193 | fmt_rec_table[file_type].end(fd);
194 | fd = -1;
195 | }
196 | stream = -1;
197 | }
198 | if (fd > 1) {
199 | close(fd);
200 | fd = -1;
201 | }
202 | if (handle && sig != SIGABRT) {
203 | snd_pcm_close(handle);
204 | handle = NULL;
205 | }
206 | exit(EXIT_FAILURE);
207 | }
208 |
209 | enum {
210 | OPT_VERSION = 1,
211 | OPT_PERIOD_SIZE,
212 | OPT_BUFFER_SIZE,
213 | OPT_DISABLE_RESAMPLE,
214 | OPT_DISABLE_CHANNELS,
215 | OPT_DISABLE_FORMAT,
216 | OPT_DISABLE_SOFTVOL,
217 | OPT_TEST_POSITION
218 | };
219 |
220 | int main()
221 | {
222 | run("b.wav");
223 | return 0;
224 | }
225 |
226 | void stop()
227 | {
228 | capture_stop = 1;
229 | }
230 |
231 | int run(char *filename)
232 | {
233 | capture_stop = 0;
234 | char *pcm_name = "default";
235 | int tmp, err;
236 | snd_pcm_info_t *info;
237 |
238 | snd_pcm_info_alloca(&info);
239 |
240 | err = snd_output_stdio_attach(&log, stderr, 0);
241 | assert(err >= 0);
242 |
243 | file_type = FORMAT_DEFAULT;
244 | stream = SND_PCM_STREAM_CAPTURE;
245 | file_type = FORMAT_WAVE;
246 | command = "arecord";
247 | start_delay = 1;
248 |
249 | chunk_size = -1;
250 | rhwparams.format = DEFAULT_FORMAT;
251 | rhwparams.rate = DEFAULT_SPEED;
252 | rhwparams.channels = 1;
253 |
254 | file_type = FORMAT_WAVE;
255 |
256 | // cdr:
257 | // rhwparams.format = SND_PCM_FORMAT_S16_BE;
258 | rhwparams.format = file_type == FORMAT_AU ? SND_PCM_FORMAT_S16_BE : SND_PCM_FORMAT_S16_LE;
259 | rhwparams.rate = 44100;
260 | rhwparams.channels = 2;
261 |
262 | err = snd_pcm_open(&handle, pcm_name, stream, open_mode);
263 | if (err < 0) {
264 | error(_("audio open error: %s"), snd_strerror(err));
265 | return 1;
266 | }
267 |
268 | if ((err = snd_pcm_info(handle, info)) < 0) {
269 | error(_("info error: %s"), snd_strerror(err));
270 | return 1;
271 | }
272 |
273 | if (nonblock) {
274 | err = snd_pcm_nonblock(handle, 1);
275 | if (err < 0) {
276 | error(_("nonblock setting error: %s"), snd_strerror(err));
277 | return 1;
278 | }
279 | }
280 |
281 | chunk_size = 1024;
282 | hwparams = rhwparams;
283 |
284 | audiobuf = (u_char *)malloc(1024);
285 | if (audiobuf == NULL) {
286 | error(_("not enough memory"));
287 | return 1;
288 | }
289 |
290 | writei_func = snd_pcm_writei;
291 | readi_func = snd_pcm_readi;
292 | writen_func = snd_pcm_writen;
293 | readn_func = snd_pcm_readn;
294 |
295 |
296 | //signal(SIGINT, signal_handler);
297 | //signal(SIGTERM, signal_handler);
298 | //signal(SIGABRT, signal_handler);
299 | capture(filename);
300 |
301 | if (fmt_rec_table[file_type].end) {
302 | fmt_rec_table[file_type].end(fd);
303 | fd = -1;
304 | }
305 | stream = -1;
306 | if (fd > 1) {
307 | close(fd);
308 | fd = -1;
309 | }
310 | if (handle) {
311 | snd_pcm_close(handle);
312 | handle = NULL;
313 | }
314 | //snd_pcm_close(handle);
315 | //free(audiobuf);
316 | //snd_output_close(log);
317 | //snd_config_update_free_global();
318 | return EXIT_SUCCESS;
319 | }
320 |
321 | static void set_params(void)
322 | {
323 | snd_pcm_hw_params_t *params;
324 | snd_pcm_sw_params_t *swparams;
325 | snd_pcm_uframes_t buffer_size;
326 | int err;
327 | size_t n;
328 | unsigned int rate;
329 | snd_pcm_uframes_t start_threshold, stop_threshold;
330 | snd_pcm_hw_params_alloca(¶ms);
331 | snd_pcm_sw_params_alloca(&swparams);
332 | err = snd_pcm_hw_params_any(handle, params);
333 | if (err < 0) {
334 | error(_("Broken configuration for this PCM: no configurations available"));
335 | exit(EXIT_FAILURE);
336 | }
337 | else if (interleaved)
338 | err = snd_pcm_hw_params_set_access(handle, params,
339 | SND_PCM_ACCESS_RW_INTERLEAVED);
340 | else
341 | err = snd_pcm_hw_params_set_access(handle, params,
342 | SND_PCM_ACCESS_RW_NONINTERLEAVED);
343 | if (err < 0) {
344 | error(_("Access type not available"));
345 | exit(EXIT_FAILURE);
346 | }
347 | err = snd_pcm_hw_params_set_format(handle, params, hwparams.format);
348 | if (err < 0) {
349 | error(_("Sample format non available"));
350 | exit(EXIT_FAILURE);
351 | }
352 | err = snd_pcm_hw_params_set_channels(handle, params, hwparams.channels);
353 | if (err < 0) {
354 | error(_("Channels count non available"));
355 | exit(EXIT_FAILURE);
356 | }
357 |
358 | #if 0
359 | err = snd_pcm_hw_params_set_periods_min(handle, params, 2);
360 | assert(err >= 0);
361 | #endif
362 | rate = hwparams.rate;
363 | err = snd_pcm_hw_params_set_rate_near(handle, params, &hwparams.rate, 0);
364 | assert(err >= 0);
365 | if ((float)rate * 1.05 < hwparams.rate || (float)rate * 0.95 > hwparams.rate) {
366 | if (!quiet_mode) {
367 | char plugex[64];
368 | const char *pcmname = snd_pcm_name(handle);
369 | fprintf(stderr, _("Warning: rate is not accurate (requested = %iHz, got = %iHz)\n"), rate, hwparams.rate);
370 | if (! pcmname || strchr(snd_pcm_name(handle), ':'))
371 | *plugex = 0;
372 | else
373 | snprintf(plugex, sizeof(plugex), "(-Dplug:%s)",
374 | snd_pcm_name(handle));
375 | fprintf(stderr, _(" please, try the plug plugin %s\n"),
376 | plugex);
377 | }
378 | }
379 | rate = hwparams.rate;
380 | if (buffer_time == 0 && buffer_frames == 0) {
381 | err = snd_pcm_hw_params_get_buffer_time_max(params,
382 | &buffer_time, 0);
383 | assert(err >= 0);
384 | if (buffer_time > 500000)
385 | buffer_time = 500000;
386 | }
387 | if (period_time == 0 && period_frames == 0) {
388 | if (buffer_time > 0)
389 | period_time = buffer_time / 4;
390 | else
391 | period_frames = buffer_frames / 4;
392 | }
393 | if (period_time > 0)
394 | err = snd_pcm_hw_params_set_period_time_near(handle, params,
395 | &period_time, 0);
396 | else
397 | err = snd_pcm_hw_params_set_period_size_near(handle, params,
398 | &period_frames, 0);
399 | assert(err >= 0);
400 | if (buffer_time > 0) {
401 | err = snd_pcm_hw_params_set_buffer_time_near(handle, params,
402 | &buffer_time, 0);
403 | } else {
404 | err = snd_pcm_hw_params_set_buffer_size_near(handle, params,
405 | &buffer_frames);
406 | }
407 | assert(err >= 0);
408 | err = snd_pcm_hw_params(handle, params);
409 | if (err < 0) {
410 | error(_("Unable to install hw params:"));
411 | snd_pcm_hw_params_dump(params, log);
412 | exit(EXIT_FAILURE);
413 | }
414 | snd_pcm_hw_params_get_period_size(params, &chunk_size, 0);
415 | snd_pcm_hw_params_get_buffer_size(params, &buffer_size);
416 | if (chunk_size == buffer_size) {
417 | error(_("Can't use period equal to buffer size (%lu == %lu)"),
418 | chunk_size, buffer_size);
419 | exit(EXIT_FAILURE);
420 | }
421 | snd_pcm_sw_params_current(handle, swparams);
422 | if (avail_min < 0)
423 | n = chunk_size;
424 | else
425 | n = (double) rate * avail_min / 1000000;
426 | err = snd_pcm_sw_params_set_avail_min(handle, swparams, n);
427 |
428 | /* round up to closest transfer boundary */
429 | n = buffer_size;
430 | if (start_delay <= 0) {
431 | start_threshold = n + (double) rate * start_delay / 1000000;
432 | } else
433 | start_threshold = (double) rate * start_delay / 1000000;
434 | if (start_threshold < 1)
435 | start_threshold = 1;
436 | if (start_threshold > n)
437 | start_threshold = n;
438 | err = snd_pcm_sw_params_set_start_threshold(handle, swparams, start_threshold);
439 | assert(err >= 0);
440 | if (stop_delay <= 0)
441 | stop_threshold = buffer_size + (double) rate * stop_delay / 1000000;
442 | else
443 | stop_threshold = (double) rate * stop_delay / 1000000;
444 | err = snd_pcm_sw_params_set_stop_threshold(handle, swparams, stop_threshold);
445 | assert(err >= 0);
446 |
447 | if (snd_pcm_sw_params(handle, swparams) < 0) {
448 | error(_("unable to install sw params:"));
449 | snd_pcm_sw_params_dump(swparams, log);
450 | exit(EXIT_FAILURE);
451 | }
452 |
453 | if (verbose)
454 | snd_pcm_dump(handle, log);
455 |
456 | bits_per_sample = snd_pcm_format_physical_width(hwparams.format);
457 | bits_per_frame = bits_per_sample * hwparams.channels;
458 | chunk_bytes = chunk_size * bits_per_frame / 8;
459 | audiobuf = realloc(audiobuf, chunk_bytes);
460 | if (audiobuf == NULL) {
461 | error(_("not enough memory"));
462 | exit(EXIT_FAILURE);
463 | }
464 | // fprintf(stderr, "real chunk_size = %i, frags = %i, total = %i\n", chunk_size, setup.buf.block.frags, setup.buf.block.frags * chunk_size);
465 |
466 | /* stereo VU-meter isn't always available... */
467 | if (vumeter == VUMETER_STEREO) {
468 | if (hwparams.channels != 2 || !interleaved || verbose > 2)
469 | vumeter = VUMETER_MONO;
470 | }
471 |
472 | buffer_frames = buffer_size; /* for position test */
473 | }
474 |
475 | #ifndef timersub
476 | #define timersub(a, b, result) \
477 | do { \
478 | (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \
479 | (result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \
480 | if ((result)->tv_usec < 0) { \
481 | --(result)->tv_sec; \
482 | (result)->tv_usec += 1000000; \
483 | } \
484 | } while (0)
485 | #endif
486 |
487 | /* I/O error handler */
488 | static void xrun(void)
489 | {
490 | snd_pcm_status_t *status;
491 | int res;
492 |
493 | snd_pcm_status_alloca(&status);
494 | if ((res = snd_pcm_status(handle, status))<0) {
495 | error(_("status error: %s"), snd_strerror(res));
496 | exit(EXIT_FAILURE);
497 | }
498 | if (snd_pcm_status_get_state(status) == SND_PCM_STATE_XRUN) {
499 | struct timeval now, diff, tstamp;
500 | gettimeofday(&now, 0);
501 | snd_pcm_status_get_trigger_tstamp(status, &tstamp);
502 | timersub(&now, &tstamp, &diff);
503 | fprintf(stderr, _("%s!!! (at least %.3f ms long)\n"),
504 | stream == SND_PCM_STREAM_PLAYBACK ? _("underrun") : _("overrun"),
505 | diff.tv_sec * 1000 + diff.tv_usec / 1000.0);
506 | if (verbose) {
507 | fprintf(stderr, _("Status:\n"));
508 | snd_pcm_status_dump(status, log);
509 | }
510 | if ((res = snd_pcm_prepare(handle))<0) {
511 | error(_("xrun: prepare error: %s"), snd_strerror(res));
512 | exit(EXIT_FAILURE);
513 | }
514 | return; /* ok, data should be accepted again */
515 | } if (snd_pcm_status_get_state(status) == SND_PCM_STATE_DRAINING) {
516 | if (verbose) {
517 | fprintf(stderr, _("Status(DRAINING):\n"));
518 | snd_pcm_status_dump(status, log);
519 | }
520 | if (stream == SND_PCM_STREAM_CAPTURE) {
521 | fprintf(stderr, _("capture stream format change? attempting recover...\n"));
522 | if ((res = snd_pcm_prepare(handle))<0) {
523 | error(_("xrun(DRAINING): prepare error: %s"), snd_strerror(res));
524 | exit(EXIT_FAILURE);
525 | }
526 | return;
527 | }
528 | }
529 | if (verbose) {
530 | fprintf(stderr, _("Status(R/W):\n"));
531 | snd_pcm_status_dump(status, log);
532 | }
533 | error(_("read/write error, state = %s"), snd_pcm_state_name(snd_pcm_status_get_state(status)));
534 | exit(EXIT_FAILURE);
535 | }
536 |
537 | /* I/O suspend handler */
538 | static void suspend(void)
539 | {
540 | int res;
541 |
542 | if (!quiet_mode)
543 | fprintf(stderr, _("Suspended. Trying resume. ")); fflush(stderr);
544 | while ((res = snd_pcm_resume(handle)) == -EAGAIN)
545 | sleep(1); /* wait until suspend flag is released */
546 | if (res < 0) {
547 | if (!quiet_mode)
548 | fprintf(stderr, _("Failed. Restarting stream. ")); fflush(stderr);
549 | if ((res = snd_pcm_prepare(handle)) < 0) {
550 | error(_("suspend: prepare error: %s"), snd_strerror(res));
551 | exit(EXIT_FAILURE);
552 | }
553 | }
554 | if (!quiet_mode)
555 | fprintf(stderr, _("Done.\n"));
556 | }
557 |
558 | static void print_vu_meter_mono(int perc, int maxperc)
559 | {
560 | const int bar_length = 50;
561 | char line[80];
562 | int val;
563 |
564 | for (val = 0; val <= perc * bar_length / 100 && val < bar_length; val++)
565 | line[val] = '#';
566 | for (; val <= maxperc * bar_length / 100 && val < bar_length; val++)
567 | line[val] = ' ';
568 | line[val] = '+';
569 | for (++val; val <= bar_length; val++)
570 | line[val] = ' ';
571 | if (maxperc > 99)
572 | sprintf(line + val, "| MAX");
573 | else
574 | sprintf(line + val, "| %02i%%", maxperc);
575 | fputs(line, stdout);
576 | if (perc > 100)
577 | printf(_(" !clip "));
578 | }
579 |
580 | static void print_vu_meter_stereo(int *perc, int *maxperc)
581 | {
582 | const int bar_length = 35;
583 | char line[80];
584 | int c;
585 |
586 | memset(line, ' ', sizeof(line) - 1);
587 | line[bar_length + 3] = '|';
588 |
589 | for (c = 0; c < 2; c++) {
590 | int p = perc[c] * bar_length / 100;
591 | char tmp[4];
592 | if (p > bar_length)
593 | p = bar_length;
594 | if (c)
595 | memset(line + bar_length + 6 + 1, '#', p);
596 | else
597 | memset(line + bar_length - p - 1, '#', p);
598 | p = maxperc[c] * bar_length / 100;
599 | if (p > bar_length)
600 | p = bar_length;
601 | if (c)
602 | line[bar_length + 6 + 1 + p] = '+';
603 | else
604 | line[bar_length - p - 1] = '+';
605 | if (maxperc[c] > 99)
606 | sprintf(tmp, "MAX");
607 | else
608 | sprintf(tmp, "%02d%%", maxperc[c]);
609 | if (c)
610 | memcpy(line + bar_length + 3 + 1, tmp, 3);
611 | else
612 | memcpy(line + bar_length, tmp, 3);
613 | }
614 | line[bar_length * 2 + 6 + 2] = 0;
615 | fputs(line, stdout);
616 | }
617 |
618 | static void print_vu_meter(signed int *perc, signed int *maxperc)
619 | {
620 | if (vumeter == VUMETER_STEREO)
621 | print_vu_meter_stereo(perc, maxperc);
622 | else
623 | print_vu_meter_mono(*perc, *maxperc);
624 | }
625 |
626 | /* peak handler */
627 | static void compute_max_peak(u_char *data, size_t count)
628 | {
629 | signed int val, max, perc[2], max_peak[2];
630 | static int run = 0;
631 | size_t ocount = count;
632 | int format_little_endian = snd_pcm_format_little_endian(hwparams.format);
633 | int ichans, c;
634 |
635 | if (vumeter == VUMETER_STEREO)
636 | ichans = 2;
637 | else
638 | ichans = 1;
639 |
640 | memset(max_peak, 0, sizeof(max_peak));
641 | switch (bits_per_sample) {
642 | case 8: {
643 | signed char *valp = (signed char *)data;
644 | signed char mask = snd_pcm_format_silence(hwparams.format);
645 | c = 0;
646 | while (count-- > 0) {
647 | val = *valp++ ^ mask;
648 | val = abs(val);
649 | if (max_peak[c] < val)
650 | max_peak[c] = val;
651 | if (vumeter == VUMETER_STEREO)
652 | c = !c;
653 | }
654 | break;
655 | }
656 | case 16: {
657 | signed short *valp = (signed short *)data;
658 | signed short mask = snd_pcm_format_silence_16(hwparams.format);
659 | signed short sval;
660 |
661 | count /= 2;
662 | c = 0;
663 | while (count-- > 0) {
664 | if (format_little_endian)
665 | sval = __le16_to_cpu(*valp);
666 | else
667 | sval = __be16_to_cpu(*valp);
668 | sval = abs(sval) ^ mask;
669 | if (max_peak[c] < sval)
670 | max_peak[c] = sval;
671 | valp++;
672 | if (vumeter == VUMETER_STEREO)
673 | c = !c;
674 | }
675 | break;
676 | }
677 | case 24: {
678 | unsigned char *valp = data;
679 | signed int mask = snd_pcm_format_silence_32(hwparams.format);
680 |
681 | count /= 3;
682 | c = 0;
683 | while (count-- > 0) {
684 | if (format_little_endian) {
685 | val = valp[0] | (valp[1]<<8) | (valp[2]<<16);
686 | } else {
687 | val = (valp[0]<<16) | (valp[1]<<8) | valp[2];
688 | }
689 | /* Correct signed bit in 32-bit value */
690 | if (val & (1<<(bits_per_sample-1))) {
691 | val |= 0xff<<24; /* Negate upper bits too */
692 | }
693 | val = abs(val) ^ mask;
694 | if (max_peak[c] < val)
695 | max_peak[c] = val;
696 | valp += 3;
697 | if (vumeter == VUMETER_STEREO)
698 | c = !c;
699 | }
700 | break;
701 | }
702 | case 32: {
703 | signed int *valp = (signed int *)data;
704 | signed int mask = snd_pcm_format_silence_32(hwparams.format);
705 |
706 | count /= 4;
707 | c = 0;
708 | while (count-- > 0) {
709 | if (format_little_endian)
710 | val = __le32_to_cpu(*valp);
711 | else
712 | val = __be32_to_cpu(*valp);
713 | val = abs(val) ^ mask;
714 | if (max_peak[c] < val)
715 | max_peak[c] = val;
716 | valp++;
717 | if (vumeter == VUMETER_STEREO)
718 | c = !c;
719 | }
720 | break;
721 | }
722 | default:
723 | if (run == 0) {
724 | fprintf(stderr, _("Unsupported bit size %d.\n"), (int)bits_per_sample);
725 | run = 1;
726 | }
727 | return;
728 | }
729 | max = 1 << (bits_per_sample-1);
730 | if (max <= 0)
731 | max = 0x7fffffff;
732 |
733 | for (c = 0; c < ichans; c++) {
734 | if (bits_per_sample > 16)
735 | perc[c] = max_peak[c] / (max / 100);
736 | else
737 | perc[c] = max_peak[c] * 100 / max;
738 | }
739 |
740 | if (interleaved && verbose <= 2) {
741 | static int maxperc[2];
742 | static time_t t=0;
743 | const time_t tt=time(NULL);
744 | if(tt>t) {
745 | t=tt;
746 | maxperc[0] = 0;
747 | maxperc[1] = 0;
748 | }
749 | for (c = 0; c < ichans; c++)
750 | if (perc[c] > maxperc[c])
751 | maxperc[c] = perc[c];
752 |
753 | putchar('\r');
754 | print_vu_meter(perc, maxperc);
755 | fflush(stdout);
756 | }
757 | else if(verbose==3) {
758 | printf(_("Max peak (%li samples): 0x%08x "), (long)ocount, max_peak[0]);
759 | for (val = 0; val < 20; val++)
760 | if (val <= perc[0] / 5)
761 | putchar('#');
762 | else
763 | putchar(' ');
764 | printf(" %i%%\n", perc[0]);
765 | fflush(stdout);
766 | }
767 | }
768 |
769 | /*
770 | * read function
771 | */
772 |
773 | static ssize_t pcm_read(u_char *data, size_t rcount)
774 | {
775 | ssize_t r;
776 | size_t result = 0;
777 | size_t count = rcount;
778 |
779 | if (count != chunk_size) {
780 | count = chunk_size;
781 | }
782 |
783 | while (count > 0) {
784 | r = readi_func(handle, data, count);
785 | if (r == -EAGAIN || (r >= 0 && (size_t)r < count)) {
786 | snd_pcm_wait(handle, 1000);
787 | } else if (r == -EPIPE) {
788 | xrun();
789 | } else if (r == -ESTRPIPE) {
790 | suspend();
791 | } else if (r < 0) {
792 | error(_("read error: %s"), snd_strerror(r));
793 | exit(EXIT_FAILURE);
794 | }
795 | if (r > 0) {
796 | if (vumeter)
797 | compute_max_peak(data, r * hwparams.channels);
798 | result += r;
799 | count -= r;
800 | data += r * bits_per_frame / 8;
801 | }
802 | }
803 | return rcount;
804 | }
805 |
806 | /* setting the globals for playing raw data */
807 | static void init_raw_data(void)
808 | {
809 | hwparams = rhwparams;
810 | }
811 |
812 | /* calculate the data count to read from/to dsp */
813 | static off64_t calc_count(void)
814 | {
815 | off64_t count;
816 |
817 | if (timelimit == 0) {
818 | count = pbrec_count;
819 | } else {
820 | count = snd_pcm_format_size(hwparams.format, hwparams.rate * hwparams.channels);
821 | count *= (off64_t)timelimit;
822 | }
823 | return count < pbrec_count ? count : pbrec_count;
824 | }
825 |
826 | /* write a WAVE-header */
827 | static void begin_wave(int fd, size_t cnt)
828 | {
829 | WaveHeader h;
830 | WaveFmtBody f;
831 | WaveChunkHeader cf, cd;
832 | int bits;
833 | u_int tmp;
834 | u_short tmp2;
835 |
836 | /* WAVE cannot handle greater than 32bit (signed?) int */
837 | if (cnt == (size_t)-2)
838 | cnt = 0x7fffff00;
839 |
840 | bits = 8;
841 | switch ((unsigned long) hwparams.format) {
842 | case SND_PCM_FORMAT_U8:
843 | bits = 8;
844 | break;
845 | case SND_PCM_FORMAT_S16_LE:
846 | bits = 16;
847 | break;
848 | case SND_PCM_FORMAT_S32_LE:
849 | case SND_PCM_FORMAT_FLOAT_LE:
850 | bits = 32;
851 | break;
852 | case SND_PCM_FORMAT_S24_LE:
853 | case SND_PCM_FORMAT_S24_3LE:
854 | bits = 24;
855 | break;
856 | default:
857 | error(_("Wave doesn't support %s format..."), snd_pcm_format_name(hwparams.format));
858 | exit(EXIT_FAILURE);
859 | }
860 | h.magic = WAV_RIFF;
861 | tmp = cnt + sizeof(WaveHeader) + sizeof(WaveChunkHeader) + sizeof(WaveFmtBody) + sizeof(WaveChunkHeader) - 8;
862 | h.length = LE_INT(tmp);
863 | h.type = WAV_WAVE;
864 |
865 | cf.type = WAV_FMT;
866 | cf.length = LE_INT(16);
867 |
868 | if (hwparams.format == SND_PCM_FORMAT_FLOAT_LE)
869 | f.format = LE_SHORT(WAV_FMT_IEEE_FLOAT);
870 | else
871 | f.format = LE_SHORT(WAV_FMT_PCM);
872 | f.channels = LE_SHORT(hwparams.channels);
873 | f.sample_fq = LE_INT(hwparams.rate);
874 | #if 0
875 | tmp2 = (samplesize == 8) ? 1 : 2;
876 | f.byte_p_spl = LE_SHORT(tmp2);
877 | tmp = dsp_speed * hwparams.channels * (u_int) tmp2;
878 | #else
879 | tmp2 = hwparams.channels * snd_pcm_format_physical_width(hwparams.format) / 8;
880 | f.byte_p_spl = LE_SHORT(tmp2);
881 | tmp = (u_int) tmp2 * hwparams.rate;
882 | #endif
883 | f.byte_p_sec = LE_INT(tmp);
884 | f.bit_p_spl = LE_SHORT(bits);
885 |
886 | cd.type = WAV_DATA;
887 | cd.length = LE_INT(cnt);
888 |
889 | if (write(fd, &h, sizeof(WaveHeader)) != sizeof(WaveHeader) ||
890 | write(fd, &cf, sizeof(WaveChunkHeader)) != sizeof(WaveChunkHeader) ||
891 | write(fd, &f, sizeof(WaveFmtBody)) != sizeof(WaveFmtBody) ||
892 | write(fd, &cd, sizeof(WaveChunkHeader)) != sizeof(WaveChunkHeader)) {
893 | error(_("write error"));
894 | exit(EXIT_FAILURE);
895 | }
896 | }
897 |
898 | static void end_wave(int fd)
899 | { /* only close output */
900 | WaveChunkHeader cd;
901 | off64_t length_seek;
902 | off64_t filelen;
903 | u_int rifflen;
904 |
905 | length_seek = sizeof(WaveHeader) +
906 | sizeof(WaveChunkHeader) +
907 | sizeof(WaveFmtBody);
908 | cd.type = WAV_DATA;
909 | cd.length = fdcount > 0x7fffffff ? LE_INT(0x7fffffff) : LE_INT(fdcount);
910 | filelen = fdcount + 2*sizeof(WaveChunkHeader) + sizeof(WaveFmtBody) + 4;
911 | rifflen = filelen > 0x7fffffff ? LE_INT(0x7fffffff) : LE_INT(filelen);
912 | if (lseek64(fd, 4, SEEK_SET) == 4)
913 | write(fd, &rifflen, 4);
914 | if (lseek64(fd, length_seek, SEEK_SET) == length_seek)
915 | write(fd, &cd, sizeof(WaveChunkHeader));
916 | if (fd != 1)
917 | close(fd);
918 | }
919 |
920 | static int new_capture_file(char *name, char *namebuf, size_t namelen,
921 | int filecount)
922 | {
923 | /* get a copy of the original filename */
924 | char *s;
925 | char buf[PATH_MAX+1];
926 |
927 | strncpy(buf, name, sizeof(buf));
928 |
929 | /* separate extension from filename */
930 | s = buf + strlen(buf);
931 | while (s > buf && *s != '.' && *s != '/')
932 | --s;
933 | if (*s == '.')
934 | *s++ = 0;
935 | else if (*s == '/')
936 | s = buf + strlen(buf);
937 |
938 | /* upon first jump to this if block rename the first file */
939 | if (filecount == 1) {
940 | if (*s)
941 | snprintf(namebuf, namelen, "%s-01.%s", buf, s);
942 | else
943 | snprintf(namebuf, namelen, "%s-01", buf);
944 | remove(namebuf);
945 | rename(name, namebuf);
946 | filecount = 2;
947 | }
948 |
949 | /* name of the current file */
950 | if (*s)
951 | snprintf(namebuf, namelen, "%s-%02i.%s", buf, filecount, s);
952 | else
953 | snprintf(namebuf, namelen, "%s-%02i", buf, filecount);
954 |
955 | return filecount;
956 | }
957 |
958 | static void capture(char *orig_name)
959 | {
960 | int tostdout=0; /* boolean which describes output stream */
961 | int filecount=0; /* number of files written */
962 | char *name = orig_name; /* current filename */
963 | char namebuf[PATH_MAX+1];
964 | off64_t count, rest; /* number of bytes to capture */
965 |
966 | /* get number of bytes to capture */
967 | count = calc_count();
968 | if (count == 0)
969 | count = LLONG_MAX;
970 | /* WAVE-file should be even (I'm not sure), but wasting one byte
971 | isn't a problem (this can only be in 8 bit mono) */
972 | if (count < LLONG_MAX)
973 | count += count % 2;
974 | else
975 | count -= count % 2;
976 |
977 | printf("arecord: Recording audio to: %s\n", name);
978 | /* setup sound hardware */
979 | set_params();
980 |
981 | /* write to stdout? */
982 | if (!name || !strcmp(name, "-")) {
983 | fd = fileno(stdout);
984 | name = "stdout";
985 | tostdout=1;
986 | if (count > fmt_rec_table[file_type].max_filesize)
987 | count = fmt_rec_table[file_type].max_filesize;
988 | }
989 |
990 | do {
991 | /* open a file to write */
992 | if(!tostdout) {
993 | /* upon the second file we start the numbering scheme */
994 | if (filecount) {
995 | filecount = new_capture_file(orig_name, namebuf,
996 | sizeof(namebuf),
997 | filecount);
998 | name = namebuf;
999 | }
1000 |
1001 | /* open a new file */
1002 | remove(name);
1003 | if ((fd = open64(name, O_WRONLY | O_CREAT, 0644)) == -1) {
1004 | perror(name);
1005 | exit(EXIT_FAILURE);
1006 | }
1007 | filecount++;
1008 | }
1009 |
1010 | rest = count;
1011 | if (rest > fmt_rec_table[file_type].max_filesize)
1012 | rest = fmt_rec_table[file_type].max_filesize;
1013 |
1014 | /* setup sample header */
1015 | if (fmt_rec_table[file_type].start)
1016 | fmt_rec_table[file_type].start(fd, rest);
1017 |
1018 | /* capture */
1019 | fdcount = 0;
1020 | while (rest > 0 && capture_stop == 0) {
1021 | size_t c = (rest <= (off64_t)chunk_bytes) ?
1022 | (size_t)rest : chunk_bytes;
1023 | size_t f = c * 8 / bits_per_frame;
1024 | if (pcm_read(audiobuf, f) != f)
1025 | break;
1026 | if (write(fd, audiobuf, c) != c) {
1027 | perror(name);
1028 | exit(EXIT_FAILURE);
1029 | }
1030 | count -= c;
1031 | rest -= c;
1032 | fdcount += c;
1033 | }
1034 |
1035 | /* finish sample container */
1036 | if (fmt_rec_table[file_type].end && !tostdout) {
1037 | fmt_rec_table[file_type].end(fd);
1038 | fd = -1;
1039 | }
1040 |
1041 | /* repeat the loop when format is raw without timelimit or
1042 | * requested counts of data are recorded
1043 | */
1044 | } while ( ((file_type == FORMAT_RAW && !timelimit) || count > 0) &&
1045 | capture_stop == 0);
1046 | printf("arecord: Stopping capturing audio.\n");
1047 | }
1048 |
--------------------------------------------------------------------------------