mirror of
				https://gitlab.freedesktop.org/pulseaudio/pulseaudio.git
				synced 2025-10-29 05:40:23 -04:00 
			
		
		
		
	add sound file streaming
git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@171 fefdeb5f-60dc-0310-8127-8f9354f1896f
This commit is contained in:
		
							parent
							
								
									50f592b67c
								
							
						
					
					
						commit
						dfd440bd5d
					
				
					 7 changed files with 198 additions and 15 deletions
				
			
		
							
								
								
									
										3
									
								
								doc/todo
									
										
									
									
									
								
							
							
						
						
									
										3
									
								
								doc/todo
									
										
									
									
									
								
							|  | @ -13,8 +13,7 @@ | |||
| - cleanup tagstruct and modargs (add s32, pa_volume_t, pa_usec_t) | ||||
| - remove all gcc warnings | ||||
| - esd compatible startup script or personality | ||||
| - add total sample size to stat | ||||
| - implement streamed file playbacj | ||||
| - add total sample cache size to stat | ||||
| 
 | ||||
| ** later *** | ||||
| - xmlrpc/http | ||||
|  |  | |||
|  | @ -133,7 +133,8 @@ polypaudio_SOURCES = idxset.c idxset.h \ | |||
| 		autoload.c autoload.h \ | ||||
| 		xmalloc.c xmalloc.h \ | ||||
| 		subscribe.h subscribe.c \ | ||||
| 		debug.h | ||||
| 		debug.h \ | ||||
| 		sound-file-stream.c sound-file-stream.h | ||||
| 
 | ||||
| polypaudio_CFLAGS = $(AM_CFLAGS) $(LIBSAMPLERATE_CFLAGS) $(LIBSNDFILE_CFLAGS) | ||||
| polypaudio_INCLUDES = $(INCLTDL) | ||||
|  |  | |||
|  | @ -46,6 +46,7 @@ | |||
| #include "play-memchunk.h" | ||||
| #include "autoload.h" | ||||
| #include "xmalloc.h" | ||||
| #include "sound-file-stream.h" | ||||
| 
 | ||||
| struct command { | ||||
|     const char *name; | ||||
|  | @ -516,10 +517,7 @@ static int pa_cli_command_scache_load(struct pa_core *c, struct pa_tokenizer *t, | |||
| 
 | ||||
| static int pa_cli_command_play_file(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) { | ||||
|     const char *fname, *sink_name; | ||||
|     struct pa_memchunk chunk; | ||||
|     struct pa_sample_spec ss; | ||||
|     struct pa_sink *sink; | ||||
|     int ret; | ||||
|     assert(c && t && buf && fail && verbose); | ||||
| 
 | ||||
|     if (!(fname = pa_tokenizer_get(t, 1)) || !(sink_name = pa_tokenizer_get(t, 2))) { | ||||
|  | @ -532,14 +530,8 @@ static int pa_cli_command_play_file(struct pa_core *c, struct pa_tokenizer *t, s | |||
|         return -1; | ||||
|     } | ||||
| 
 | ||||
|     if (pa_sound_file_load(fname, &ss, &chunk, c->memblock_stat) < 0) { | ||||
|         pa_strbuf_puts(buf, "Failed to load sound file.\n"); | ||||
|         return -1; | ||||
|     } | ||||
| 
 | ||||
|     ret = pa_play_memchunk(sink, fname, &ss, &chunk, PA_VOLUME_NORM); | ||||
|     pa_memblock_unref(chunk.memblock); | ||||
|     return ret; | ||||
|     return pa_play_file(sink, fname, PA_VOLUME_NORM); | ||||
| } | ||||
| 
 | ||||
| static int pa_cli_command_autoload_add(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) { | ||||
|  |  | |||
|  | @ -76,7 +76,7 @@ static void sink_input_drop(struct pa_sink_input *i, const struct pa_memchunk*ch | |||
|         pa_mainloop_api_once(i->sink->core->mainloop, si_kill, i); | ||||
| } | ||||
| 
 | ||||
| int pa_play_memchunk(struct pa_sink *sink, const char *name, const struct pa_sample_spec *ss, const struct pa_memchunk *chunk, uint32_t volume) { | ||||
| int pa_play_memchunk(struct pa_sink *sink, const char *name, const struct pa_sample_spec *ss, const struct pa_memchunk *chunk, pa_volume_t volume) { | ||||
|     struct pa_sink_input *si; | ||||
|     struct pa_memchunk *nchunk; | ||||
| 
 | ||||
|  |  | |||
|  | @ -25,6 +25,6 @@ | |||
| #include "sink.h" | ||||
| #include "memchunk.h" | ||||
| 
 | ||||
| int pa_play_memchunk(struct pa_sink *sink, const char *name, const struct pa_sample_spec *ss, const struct pa_memchunk *chunk, uint32_t volume); | ||||
| int pa_play_memchunk(struct pa_sink *sink, const char *name, const struct pa_sample_spec *ss, const struct pa_memchunk *chunk, pa_volume_t volume); | ||||
| 
 | ||||
| #endif | ||||
|  |  | |||
							
								
								
									
										162
									
								
								polyp/sound-file-stream.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										162
									
								
								polyp/sound-file-stream.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,162 @@ | |||
| /* $Id$ */ | ||||
| 
 | ||||
| /***
 | ||||
|   This file is part of polypaudio. | ||||
|   | ||||
|   polypaudio is free software; you can redistribute it and/or modify | ||||
|   it under the terms of the GNU General Public License as published | ||||
|   by the Free Software Foundation; either version 2 of the License, | ||||
|   or (at your option) any later version. | ||||
|   | ||||
|   polypaudio is distributed in the hope that it will be useful, but | ||||
|   WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||||
|   General Public License for more details. | ||||
|   | ||||
|   You should have received a copy of the GNU General Public License | ||||
|   along with polypaudio; if not, write to the Free Software | ||||
|   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 | ||||
|   USA. | ||||
| ***/ | ||||
| 
 | ||||
| #ifdef HAVE_CONFIG_H | ||||
| #include <config.h> | ||||
| #endif | ||||
| 
 | ||||
| #include <stdlib.h> | ||||
| #include <assert.h> | ||||
| #include <stdio.h> | ||||
| #include <string.h> | ||||
| 
 | ||||
| #include <sndfile.h> | ||||
| 
 | ||||
| #include "sound-file-stream.h" | ||||
| #include "sink-input.h" | ||||
| #include "xmalloc.h" | ||||
| 
 | ||||
| #define BUF_SIZE (1024*10) | ||||
| 
 | ||||
| struct userdata { | ||||
|     SNDFILE *sndfile; | ||||
|     struct pa_sink_input *sink_input; | ||||
|     struct pa_memchunk memchunk; | ||||
| }; | ||||
| 
 | ||||
| static void free_userdata(struct userdata *u) { | ||||
|     assert(u); | ||||
|     if (u->sink_input) | ||||
|         pa_sink_input_free(u->sink_input); | ||||
|     if (u->memchunk.memblock) | ||||
|         pa_memblock_unref(u->memchunk.memblock); | ||||
|     if (u->sndfile) | ||||
|         sf_close(u->sndfile); | ||||
| 
 | ||||
|     pa_xfree(u); | ||||
| } | ||||
| 
 | ||||
| static void sink_input_kill(struct pa_sink_input *i) { | ||||
|     assert(i && i->userdata); | ||||
|     free_userdata(i->userdata); | ||||
| } | ||||
| 
 | ||||
| static void si_kill(struct pa_mainloop_api *m, void *i) { | ||||
|     sink_input_kill(i); | ||||
| } | ||||
| 
 | ||||
| static int sink_input_peek(struct pa_sink_input *i, struct pa_memchunk *chunk) { | ||||
|     struct userdata *u; | ||||
|     assert(i && chunk && i->userdata); | ||||
|     u = i->userdata; | ||||
| 
 | ||||
|     if (!u->memchunk.memblock) { | ||||
|         uint32_t fs = pa_frame_size(&i->sample_spec); | ||||
|         sf_count_t samples = BUF_SIZE/fs; | ||||
| 
 | ||||
|         u->memchunk.memblock = pa_memblock_new(BUF_SIZE, i->sink->core->memblock_stat); | ||||
|         u->memchunk.index = 0; | ||||
|         samples = sf_readf_float(u->sndfile, u->memchunk.memblock->data, samples); | ||||
|         u->memchunk.length = samples*fs; | ||||
|          | ||||
|         if (!u->memchunk.length) { | ||||
|             pa_memblock_unref(u->memchunk.memblock); | ||||
|             u->memchunk.memblock = NULL; | ||||
|             u->memchunk.index = u->memchunk.length = 0; | ||||
|             pa_mainloop_api_once(i->sink->core->mainloop, si_kill, i); | ||||
|             return -1; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     *chunk = u->memchunk; | ||||
|     pa_memblock_ref(chunk->memblock); | ||||
|     assert(chunk->length); | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| static void sink_input_drop(struct pa_sink_input *i, const struct pa_memchunk*chunk, size_t length) { | ||||
|     struct userdata *u; | ||||
|     assert(i && chunk && length && i->userdata); | ||||
|     u = i->userdata; | ||||
| 
 | ||||
|     assert(!memcmp(chunk, &u->memchunk, sizeof(chunk))); | ||||
|     assert(length <= u->memchunk.length); | ||||
| 
 | ||||
|     u->memchunk.index += length; | ||||
|     u->memchunk.length -= length; | ||||
| 
 | ||||
|     if (u->memchunk.length <= 0) { | ||||
|         pa_memblock_unref(u->memchunk.memblock); | ||||
|         u->memchunk.memblock = NULL; | ||||
|         u->memchunk.index = u->memchunk.length = 0; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| int pa_play_file(struct pa_sink *sink, const char *fname, pa_volume_t volume) { | ||||
|     struct userdata *u = NULL; | ||||
|     SF_INFO sfinfo; | ||||
|     struct pa_sample_spec ss; | ||||
|     assert(sink && fname); | ||||
| 
 | ||||
|     if (volume <= 0) | ||||
|         goto fail; | ||||
| 
 | ||||
|     u = pa_xmalloc(sizeof(struct userdata)); | ||||
|     u->sink_input = NULL; | ||||
|     u->memchunk.memblock = NULL; | ||||
|     u->memchunk.index = u->memchunk.length = 0; | ||||
|     u->sndfile = NULL; | ||||
| 
 | ||||
|     memset(&sfinfo, 0, sizeof(sfinfo)); | ||||
| 
 | ||||
|     if (!(u->sndfile = sf_open(fname, SFM_READ, &sfinfo))) { | ||||
|         fprintf(stderr, __FILE__": Failed to open file %s\n", fname); | ||||
|         goto fail; | ||||
|     } | ||||
| 
 | ||||
|     ss.format = PA_SAMPLE_FLOAT32; | ||||
|     ss.rate = sfinfo.samplerate; | ||||
|     ss.channels = sfinfo.channels; | ||||
| 
 | ||||
|     if (!pa_sample_spec_valid(&ss)) { | ||||
|         fprintf(stderr, __FILE__": Unsupported sample format in file %s\n", fname); | ||||
|         goto fail; | ||||
|     } | ||||
|      | ||||
|     if (!(u->sink_input = pa_sink_input_new(sink, fname, &ss))) | ||||
|         goto fail; | ||||
| 
 | ||||
|     u->sink_input->volume = volume; | ||||
|     u->sink_input->peek = sink_input_peek; | ||||
|     u->sink_input->drop = sink_input_drop; | ||||
|     u->sink_input->kill = sink_input_kill; | ||||
|     u->sink_input->userdata = u; | ||||
|      | ||||
|     pa_sink_notify(sink); | ||||
| 
 | ||||
|     return 0; | ||||
| 
 | ||||
| fail: | ||||
|     if (u) | ||||
|         free_userdata(u); | ||||
|      | ||||
|     return -1; | ||||
| } | ||||
							
								
								
									
										29
									
								
								polyp/sound-file-stream.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								polyp/sound-file-stream.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,29 @@ | |||
| #ifndef foosoundfilestreamhfoo | ||||
| #define foosoundfilestreamhfoo | ||||
| 
 | ||||
| /* $Id$ */ | ||||
| 
 | ||||
| /***
 | ||||
|   This file is part of polypaudio. | ||||
|   | ||||
|   polypaudio is free software; you can redistribute it and/or modify | ||||
|   it under the terms of the GNU General Public License as published | ||||
|   by the Free Software Foundation; either version 2 of the License, | ||||
|   or (at your option) any later version. | ||||
|   | ||||
|   polypaudio is distributed in the hope that it will be useful, but | ||||
|   WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||||
|   General Public License for more details. | ||||
|   | ||||
|   You should have received a copy of the GNU General Public License | ||||
|   along with polypaudio; if not, write to the Free Software | ||||
|   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 | ||||
|   USA. | ||||
| ***/ | ||||
| 
 | ||||
| #include "sink.h" | ||||
| 
 | ||||
| int pa_play_file(struct pa_sink *sink, const char *fname, pa_volume_t volume); | ||||
| 
 | ||||
| #endif | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Lennart Poettering
						Lennart Poettering