source: mainline/uspace/app/wavplay/drec.c@ 6eeaf1d

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 6eeaf1d was 0a4ad7d, checked in by Jan Vesely <jano.vesely@…>, 12 years ago

wavplay: Add direct recording functionality

  • Property mode set to 100644
File size: 6.5 KB
Line 
1/*
2 * Copyright (c) 2013 Jan Vesely
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup wavplay
30 * @{
31 */
32/**
33 * @file PCM playback audio devices
34 */
35
36#include <assert.h>
37#include <errno.h>
38#include <str_error.h>
39#include <audio_pcm_iface.h>
40#include <pcm/format.h>
41#include <stdio.h>
42#include <sys/mman.h>
43
44#include "wave.h"
45#include "drec.h"
46
47
48#define BUFFER_PARTS 2
49
50/** Recording format */
51static const pcm_format_t format = {
52 .sampling_rate = 44100,
53 .channels = 2,
54 .sample_format = PCM_SAMPLE_SINT16_LE,
55};
56
57/** Recording helper structure */
58typedef struct {
59 struct {
60 void *base;
61 size_t size;
62 unsigned id;
63 void* position;
64 } buffer;
65 FILE* file;
66 audio_pcm_sess_t *device;
67} record_t;
68
69/**
70 * Initialize recording helper structure.
71 * @param rec Recording structure.
72 * @param sess Session to IPC device.
73 */
74static void record_initialize(record_t *rec, audio_pcm_sess_t *sess)
75{
76 assert(sess);
77 assert(rec);
78 rec->buffer.base = NULL;
79 rec->buffer.size = 0;
80 rec->buffer.position = NULL;
81 rec->file = NULL;
82 rec->device = sess;
83}
84
85/**
86 * Recording callback. Writes recorded data.
87 * @param iid IPC call id.
88 * @param icall Poitner to IPC call structure.
89 * @param arg Argument. Poitner to recording helper structure.
90 */
91static void device_event_callback(ipc_callid_t iid, ipc_call_t *icall, void* arg)
92{
93 async_answer_0(iid, EOK);
94 record_t *rec = arg;
95 const size_t buffer_part = rec->buffer.size / BUFFER_PARTS;
96 bool record = true;
97 while (record) {
98 ipc_call_t call;
99 ipc_callid_t callid = async_get_call(&call);
100 switch(IPC_GET_IMETHOD(call)) {
101 case PCM_EVENT_CAPTURE_TERMINATED:
102 printf("Recording terminated\n");
103 record = false;
104 case PCM_EVENT_FRAMES_CAPTURED:
105 printf("%u frames\n", IPC_GET_ARG1(call));
106 async_answer_0(callid, EOK);
107 break;
108 default:
109 printf("Unknown event %d.\n", IPC_GET_IMETHOD(call));
110 async_answer_0(callid, ENOTSUP);
111 continue;
112
113 }
114
115 /* Write directly from device buffer to file */
116 const size_t bytes = fwrite(rec->buffer.position,
117 sizeof(uint8_t), buffer_part, rec->file);
118 printf("%zu ", bytes);
119 rec->buffer.position += buffer_part;
120
121 if (rec->buffer.position >= (rec->buffer.base + rec->buffer.size))
122 rec->buffer.position = rec->buffer.base;
123 async_answer_0(callid, EOK);
124 }
125}
126
127/**
128 * Start fragment based recording.
129 * @param rec Recording helper structure.
130 * @param f PCM format
131 */
132static void record_fragment(record_t *rec, pcm_format_t f)
133{
134 assert(rec);
135 assert(rec->device);
136 int ret = audio_pcm_register_event_callback(rec->device,
137 device_event_callback, rec);
138 if (ret != EOK) {
139 printf("Failed to register for events: %s.\n", str_error(ret));
140 return;
141 }
142 rec->buffer.position = rec->buffer.base;
143 printf("Recording: %dHz, %s, %d channel(s).\n", f.sampling_rate,
144 pcm_sample_format_str(f.sample_format), f.channels);
145 const unsigned frames =
146 pcm_format_size_to_frames(rec->buffer.size / BUFFER_PARTS, &f);
147 ret = audio_pcm_start_capture_fragment(rec->device,
148 frames, f.channels, f.sampling_rate, f.sample_format);
149 if (ret != EOK) {
150 printf("Failed to start recording: %s.\n", str_error(ret));
151 return;
152 }
153
154 getchar();
155 printf("\n");
156 audio_pcm_stop_capture(rec->device);
157}
158
159/**
160 * Record directly from a device to a file.
161 * @param device The device.
162 * @param file The file.
163 * @return Error code.
164 */
165int drecord(const char *device, const char *file)
166{
167 int ret = EOK;
168 audio_pcm_sess_t *session = NULL;
169 if (str_cmp(device, "default") == 0) {
170 session = audio_pcm_open_default();
171 } else {
172 session = audio_pcm_open(device);
173 }
174 if (!session) {
175 printf("Failed to connect to device %s.\n", device);
176 return 1;
177 }
178 printf("Recording on device: %s.\n", device);
179 if (audio_pcm_query_cap(session, AUDIO_CAP_CAPTURE) <= 0) {
180 printf("Device %s does not support recording\n", device);
181 ret = ENOTSUP;
182 goto close_session;
183 }
184
185 const char* info = NULL;
186 ret = audio_pcm_get_info_str(session, &info);
187 if (ret != EOK) {
188 printf("Failed to get PCM info.\n");
189 goto close_session;
190 }
191 printf("Capturing on %s.\n", info);
192 free(info);
193
194 record_t rec;
195 record_initialize(&rec, session);
196
197 ret = audio_pcm_get_buffer(rec.device, &rec.buffer.base,
198 &rec.buffer.size);
199 if (ret != EOK) {
200 printf("Failed to get PCM buffer: %s.\n", str_error(ret));
201 goto close_session;
202 }
203 printf("Buffer: %p %zu.\n", rec.buffer.base, rec.buffer.size);
204
205 rec.file = fopen(file, "w");
206 if (rec.file == NULL) {
207 ret = ENOENT;
208 printf("Failed to open file: %s.\n", file);
209 goto cleanup;
210 }
211
212 wave_header_t header;
213 fseek(rec.file, sizeof(header), SEEK_SET);
214 const char *error;
215 if (ret != EOK) {
216 printf("Error parsing wav header: %s.\n", error);
217 goto cleanup;
218 }
219 if (audio_pcm_query_cap(rec.device, AUDIO_CAP_INTERRUPT) > 0)
220 record_fragment(&rec, format);
221 else
222 printf("Recording method is not supported");
223 //TODO consider buffer position interface
224
225 wav_init_header(&header, format, ftell(rec.file) - sizeof(header));
226 fseek(rec.file, 0, SEEK_SET);
227 fwrite(&header, sizeof(header), 1, rec.file);
228
229cleanup:
230 fclose(rec.file);
231 munmap(rec.buffer.base, rec.buffer.size);
232 audio_pcm_release_buffer(rec.device);
233close_session:
234 audio_pcm_close(session);
235 return ret == EOK ? 0 : 1;
236}
237/**
238 * @}
239 */
Note: See TracBrowser for help on using the repository browser.