source: mainline/uspace/app/dplay/dplay.c@ ea6c838

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

Add frame count to event report.

This enables applications to detect underruns.

  • Property mode set to 100644
File size: 6.2 KB
Line 
1/*
2 * Copyright (c) 2012 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 dplay
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 <str.h>
40#include <devman.h>
41#include <audio_pcm_iface.h>
42#include <fibril_synch.h>
43#include <stdio.h>
44#include <sys/mman.h>
45#include <sys/time.h>
46
47#include <stdio.h>
48#include <macros.h>
49
50#include "wave.h"
51
52#define DEFAULT_DEVICE "/hw/pci0/00:01.0/sb16/pcm"
53#define BUFFER_PARTS 2
54
55typedef struct {
56 struct {
57 void *base;
58 size_t size;
59 void* position;
60 } buffer;
61 FILE* source;
62 volatile bool playing;
63 fibril_mutex_t mutex;
64 fibril_condvar_t cv;
65 audio_pcm_sess_t *device;
66} playback_t;
67
68static void playback_initialize(playback_t *pb, audio_pcm_sess_t *sess)
69{
70 assert(sess);
71 assert(pb);
72 pb->buffer.base = NULL;
73 pb->buffer.size = 0;
74 pb->buffer.position = NULL;
75 pb->playing = false;
76 pb->source = NULL;
77 pb->device = sess;
78 fibril_mutex_initialize(&pb->mutex);
79 fibril_condvar_initialize(&pb->cv);
80}
81
82
83static void device_event_callback(ipc_callid_t iid, ipc_call_t *icall, void* arg)
84{
85 async_answer_0(iid, EOK);
86 playback_t *pb = arg;
87 const size_t buffer_part = pb->buffer.size / BUFFER_PARTS;
88 while (1) {
89 ipc_call_t call;
90 ipc_callid_t callid = async_get_call(&call);
91 switch(IPC_GET_IMETHOD(call)) {
92 case PCM_EVENT_FRAMES_PLAYED:
93 printf("%u frames\n", IPC_GET_ARG1(call));
94 async_answer_0(callid, EOK);
95 break;
96 case PCM_EVENT_PLAYBACK_TERMINATED:
97 printf("Playback terminated\n");
98 fibril_mutex_lock(&pb->mutex);
99 pb->playing = false;
100 fibril_condvar_signal(&pb->cv);
101 async_answer_0(callid, EOK);
102 fibril_mutex_unlock(&pb->mutex);
103 return;
104 default:
105 printf("Unknown event %d.\n", IPC_GET_IMETHOD(call));
106 async_answer_0(callid, ENOTSUP);
107 continue;
108
109 }
110 const size_t bytes = fread(pb->buffer.position, sizeof(uint8_t),
111 buffer_part, pb->source);
112 if (bytes == 0) {
113 audio_pcm_stop_playback(pb->device);
114 }
115 bzero(pb->buffer.position + bytes, buffer_part - bytes);
116 pb->buffer.position += buffer_part;
117
118 if (pb->buffer.position >= (pb->buffer.base + pb->buffer.size))
119 pb->buffer.position = pb->buffer.base;
120 }
121}
122
123
124static void play(playback_t *pb, unsigned channels, unsigned sampling_rate,
125 pcm_sample_format_t format)
126{
127 assert(pb);
128 assert(pb->device);
129 pb->buffer.position = pb->buffer.base;
130 printf("Playing: %dHz, %s, %d channel(s).\n",
131 sampling_rate, pcm_sample_format_str(format), channels);
132 const size_t bytes = fread(pb->buffer.base, sizeof(uint8_t),
133 pb->buffer.size, pb->source);
134 if (bytes != pb->buffer.size)
135 bzero(pb->buffer.base + bytes, pb->buffer.size - bytes);
136 printf("Buffer data ready.\n");
137 fibril_mutex_lock(&pb->mutex);
138 const unsigned frames = pb->buffer.size /
139 (BUFFER_PARTS * channels * pcm_sample_format_size(format));
140 int ret = audio_pcm_start_playback(pb->device,
141 frames, channels, sampling_rate, format);
142 if (ret != EOK) {
143 fibril_mutex_unlock(&pb->mutex);
144 printf("Failed to start playback: %s.\n", str_error(ret));
145 return;
146 }
147
148 for (pb->playing = true; pb->playing;
149 fibril_condvar_wait(&pb->cv, &pb->mutex));
150
151 fibril_mutex_unlock(&pb->mutex);
152 printf("\n");
153}
154
155int main(int argc, char *argv[])
156{
157 const char *device = DEFAULT_DEVICE;
158 const char *file;
159 switch (argc) {
160 case 2:
161 file = argv[1];
162 break;
163 case 3:
164 device = argv[1];
165 file = argv[2];
166 break;
167 default:
168 printf("Usage: %s [device] file.\n", argv[0]);
169 return 1;
170 }
171
172 audio_pcm_sess_t *session = audio_pcm_open(device);
173 if (!session) {
174 printf("Failed to connect to device.\n");
175 return 1;
176 }
177
178 const char* info = NULL;
179 int ret = audio_pcm_get_info_str(session, &info);
180 if (ret != EOK) {
181 printf("Failed to get PCM info.\n");
182 goto close_session;
183 }
184 printf("Playing on %s.\n", info);
185 free(info);
186
187 playback_t pb;
188 playback_initialize(&pb, session);
189
190 ret = audio_pcm_get_buffer(pb.device, &pb.buffer.base,
191 &pb.buffer.size, device_event_callback, &pb);
192 if (ret != EOK) {
193 printf("Failed to get PCM buffer: %s.\n", str_error(ret));
194 goto close_session;
195 }
196 printf("Buffer: %p %zu.\n", pb.buffer.base, pb.buffer.size);
197 uintptr_t ptr = 0;
198 as_get_physical_mapping(pb.buffer.base, &ptr);
199 printf("buffer mapped at %x.\n", ptr);
200
201 pb.source = fopen(file, "rb");
202 if (pb.source == NULL) {
203 ret = ENOENT;
204 printf("Failed to open %s.\n", file);
205 goto cleanup;
206 }
207 wave_header_t header;
208 fread(&header, sizeof(header), 1, pb.source);
209 unsigned rate, channels;
210 pcm_sample_format_t format;
211 const char *error;
212 ret = wav_parse_header(&header, NULL, NULL, &channels, &rate, &format,
213 &error);
214 if (ret != EOK) {
215 printf("Error parsing wav header: %s.\n", error);
216 fclose(pb.source);
217 goto cleanup;
218 }
219
220 play(&pb, channels, rate, format);
221 fclose(pb.source);
222
223cleanup:
224 munmap(pb.buffer.base, pb.buffer.size);
225 audio_pcm_release_buffer(pb.device);
226close_session:
227 audio_pcm_close(session);
228 return ret == EOK ? 0 : 1;
229}
230/**
231 * @}
232 */
Note: See TracBrowser for help on using the repository browser.