Compare commits

..

1005 Commits

Author SHA1 Message Date
Avuton Olrich
af4a93dbcf mpd version 0.16~alpha3 2010-11-07 06:39:31 -08:00
Max Kellermann
4478b3ef74 Merge release 0.15.14 from branch 'v0.15.x'
Conflicts:
	NEWS
	configure.ac
	src/decoder_control.c
	src/decoder_control.h
	src/input/rewind_input_plugin.c
	src/output_control.c
	src/output_thread.c
	src/player_thread.c
2010-11-07 15:30:18 +01:00
Avuton Olrich
dec7090198 mpd version 0.15.14 2010-11-06 14:42:02 -07:00
Max Kellermann
83ec0e5552 player_thread: fix assertion failure due to wrong music pipe on seek
When one song is played twice, and the decoder is working on the
second "instance", but the first should be seeked, the check in
player_seek_decoder() may assume that it can reuse the decoder without
exchanging pipes.  The last thing was the mistake: the pipe pointer
was different, which led to an assertion failure.  This patch adds
another check which exchanges the player pipe.
2010-11-05 19:24:42 +01:00
Max Kellermann
cc261872c2 decoder_control: pass music_pipe to dc_start()
More abstraction for decoder_control.pipe.
2010-11-05 19:18:44 +01:00
Max Kellermann
5223261f19 player_thread: add helper function player_dc_at_next_song()
Some abstraction for decoder_control.pipe access.
2010-11-05 19:08:59 +01:00
Max Kellermann
c594afeee7 pipe: add helper function music_pipe_empty() 2010-11-05 18:40:23 +01:00
Max Kellermann
32d10eedbd input/rewind: remove redundant NULL check before g_free() call 2010-11-05 18:40:14 +01:00
Max Kellermann
dfd98eede7 input/rewind: add two assertions 2010-11-05 18:40:07 +01:00
Max Kellermann
a728d7a026 input/rewind: fix double free bug
Duplicate the "mime" attribute of the inner input_stream object,
instead of copying the pointer.
2010-11-05 18:39:40 +01:00
Max Kellermann
5a26320680 output/alsa: dump buffer and period limits 2010-11-05 10:35:46 +01:00
Max Kellermann
90dc880e67 output/httpd: implement delay() 2010-11-05 09:49:22 +01:00
Max Kellermann
e11ff967d0 output/shout: implement delay()
This makes the plugin more responsive to control commands, because it
will listen to control events while waiting.
2010-11-05 09:49:20 +01:00
Max Kellermann
2dc6ed7b3a output_plugin: add method delay()
This method is used to reduce the delay of commands issued to the
shout plugin.
2010-11-05 09:47:43 +01:00
Max Kellermann
ad430c6617 timer: add function timer_delay() 2010-11-05 09:39:56 +01:00
Max Kellermann
e8d8bd4c0d decoder/{mp4ff,ffmpeg}: add extension ".m4b" (audio book)
Same as ".m4a".
2010-11-05 02:01:35 +01:00
Max Kellermann
8d5fa754e8 output_thread: fix assertion failure due to race condition in OPEN
Change the assertion on "fail_timer==NULL" in OPEN to a runtime check.
This assertion crashed when the output thread failed while the player
thread was calling audio_output_open().
2010-11-04 23:44:23 +01:00
Max Kellermann
2ee047a1dd output_internal: protect attribute "fail_timer" with mutex 2010-11-04 23:40:43 +01:00
Max Kellermann
9562f66741 output_control: lock object in audio_output_open()
Protect the attributes "open" and "fail_timer".
2010-11-04 23:28:18 +01:00
Max Kellermann
21223154aa output_control: lock object in audio_output_close()
Protect the attributes "open" and "fail_timer".
2010-11-04 21:51:02 +01:00
Mantas Mikulenas
ec48b5ea3a server_socket: remove AI_ADDRCONFIG
When you pass the flag AI_ADDRCONFIG to getaddrinfo(), it does not
consider address families on the loopback device.  When run on a
machine without an external network card, just with "lo", it was
unable to look up any address.
2010-11-04 20:17:45 +01:00
Max Kellermann
754015544f output/ffado: transfer_playback_buffers() returns a boolean
libffado documentation says this function returns -1 on error, but
that is a lie - it returns a boolean value, and "false" means error.
2010-11-04 20:08:04 +01:00
Max Kellermann
3f89f77429 decoder/ffmpeg: check AVCodecContext.sample_fmt value
.. instead of av_get_bits_per_sample_format().  The SampleFormat enum
value is authoritative.
2010-11-04 20:04:15 +01:00
Denis Krjuchkov
9dee419b7c winmm_output: handle empty string case when parsing device id 2010-11-04 11:09:50 +05:00
Denis Krjuchkov
7612bf1bfa winmm_output: added "device" configuration option
Device can be specified either by magic index (starting with 0)
or by device name.
2010-11-04 00:51:18 +05:00
Denis Krjuchkov
ad56e10e5b winmm_output: improved test_default_device
If no device is available test_default_device returns false.
2010-11-03 23:31:49 +05:00
Max Kellermann
75f4772ba2 output: new output plugin "ffado"
Using libffado, to play on firewire audio devices.

Warning: this plugin was not tested successfully.  I just couldn't
keep libffado2 from crashing.  Use at your own risk.

For details, see my Debian bug reports:

  http://bugs.debian.org/601657
  http://bugs.debian.org/601659
2010-10-27 21:25:41 +02:00
Alder Hornbridge
fe1b626f76 decoder/sidplay: play mus, str, prg, x00 files 2010-10-27 21:18:43 +02:00
Alder Hornbridge
4e94516912 decoder/sidplay: play monaural SID tunes in mono 2010-10-27 21:16:24 +02:00
Tony Miller
dadb6747ad Container support for gme decoder. 2010-10-14 17:11:59 +02:00
Max Kellermann
188e1b440e playlist/rss: new playlist plugin for RSS feeds 2010-10-11 20:33:41 +02:00
Max Kellermann
a57f9e712d Merge release 0.15.13 from branch 'v0.15.x'
Conflicts:
	NEWS
	configure.ac
	src/input/rewind_input_plugin.c
	src/output/httpd_output_plugin.c
2010-10-11 20:33:17 +02:00
Avuton Olrich
a549d871f3 Modify version string to post-release version 0.15.14~git 2010-10-10 09:57:57 -07:00
Avuton Olrich
b552e9a120 mpd version 0.15.13 2010-10-10 09:57:52 -07:00
Denis Krjuchkov
e6fc88a758 mixer: winmm_mixer implemented 2010-10-09 02:45:08 +06:00
Denis Krjuchkov
20004b7ee0 win32_output: renamed win32 output plugin to winmm
Win32 has many audio APIs. New name is slightly more correct.
2010-10-08 23:55:14 +06:00
Max Kellermann
84e037631d output/httpd: use the new server_socket library 2010-10-05 21:18:54 +02:00
Max Kellermann
18e3d0b504 listen: move generic code to server_socket.c 2010-10-05 21:18:54 +02:00
Max Kellermann
04c4398bfc output/httpd: don't close socket in open() failure
This cleanup call is obsolete, since we moved the binding code to
enable()/disable().
2010-10-05 21:18:54 +02:00
Max Kellermann
39e42394bd output_all: disable outputs on shutdown
Call output_plugin.disable() before output_plugin.finish().  This
ensures that all outputs are properly cleaned up, to make valgrind
happy.
2010-10-05 21:18:54 +02:00
Qball Cow
a39e6b43e8 add mpd_error.h to sources. 2010-10-03 19:46:36 +02:00
Max Kellermann
5923cfcde3 output/httpd: MIME type audio/ogg for Ogg Vorbis
RFC 5334 10.3 defines the MIME type "audio/ogg".  We could use
"application/ogg" as well, but we know for sure that we only emit
audio data.
2010-10-03 16:22:03 +02:00
Tony Miller
e69df36e4a configure.ac: Disable unix domain sockets by default if we're on cygwin. 2010-10-02 13:27:02 -07:00
Denis Krjuchkov
e10b872fc3 main_win: replaced g_error usages with MPD_ERROR 2010-09-28 22:38:57 +06:00
Denis Krjuchkov
2b78358af5 mpd_error: more correct MPD_ERROR implementation
Original implementation does not handle
	if (...)
		MPD_ERROR("die");
	else
		...
case well. This change fixes handling of such cases.
2010-09-28 18:12:14 +02:00
Thomas Jansen
e3f4c7b91c input/rewind: enable for MMS 2010-09-28 12:56:47 +02:00
Andrew Morgan
a59ab3e2ee playlist: make single mode 'sticky' 2010-09-28 12:52:52 +02:00
Thomas Jansen
28bcb8bdf5 eliminate g_error() usage
Replaced all occurrences of g_error() with MPD_ERROR() located in a new header
file 'mpd_error.h'. This macro uses g_critical() to print the error message
and then exits gracefully in contrast to g_error() which would internally call
abort() to produce a core dump.

The macro name is distinctive and allows to find all places with dubious error
handling. The long-term goal is to get rid of MPD_ERROR() altogether. To
facilitate the eventual removal of this macro it was added in a new header
file rather than to an existing header file.

This fixes #2995 and #3007.
2010-09-25 15:00:43 +02:00
Thomas Jansen
9af9fd1400 output/httpd: bind_to_address support (including IPv6)
Added support for a new optional configuration setting for the httpd output
named "bind_to_address". Setting it to a specific IP address (v4 or v6) will
cause the httpd output to bind to that address exclusively. Supporting
multiple addresses in parallel is future work.

This implements the feature requests #2998 and #2646.
2010-09-25 15:00:43 +02:00
Thomas Jansen
0c80bd5fc0 conf: Whitespace cosmetics 2010-09-25 15:00:43 +02:00
Denis Krjuchkov
7563ece236 .gitignore: added mpd.exe to ignore list 2010-09-25 15:01:53 +06:00
Max Kellermann
a14cd97f56 playlist: fix "queued" check in playlist_sync()
The check was meant to fix an assertion failure, but it was the wrong
way around.  This broke cross-fading most of the time.
2010-09-23 23:29:36 +02:00
Max Kellermann
0955f33a86 decoder/mp4ff: support more variations of "album artist"
According to the mantis bug report 2847, there are several possible
variations of the "album artist" tag:

- "album artist"
- "album_artist"
- "albumartist"

This patch adds support for the latter two.
2010-09-23 21:19:41 +02:00
Max Kellermann
a016fb4048 listen: fix "unused parameter" warning on WIN32 2010-09-23 20:51:23 +02:00
Denis Krjuchkov
e8ebb1af91 main: Add Windows Service support
I've added PIPE_EVENT_SHUTDOWN because calling g_main_loop_quit() do not work when called from another thread.
Main thread was sleeping in g_poll() so I needed some way to wake it up.

By some strange reason call close(event_pipe[0]) in event_pipe_deinit() hangs.
In current implementation that code never reached so that was not a problem :-)
I've added a conditional to leave event_pipe[0] open on Win32.
2010-09-23 20:42:33 +02:00
Thomas Jansen
9fa3d7c4fa playlist_state: Fix the "state" line in the output
An '\n' was erroneously inserted in the line containing the state, e.g.
"state: \nplay" instead of "state: play".

Fix for bug #2992.
2010-09-23 20:41:23 +02:00
Thomas Jansen
54294366d5 rewind_input_plugin: Update MIME not only once
The assumption that MIME type is set only once is not valid with CURL,
as URL redirections may update the MIME type.

This fixes bug #3044.
2010-09-23 20:39:13 +02:00
Max Kellermann
9423b456a1 zeroconf-bonjour: use g_htons() instead of htons()
htons() is not available if netinet/in.h is not included.
2010-09-23 09:01:37 +02:00
Max Kellermann
64209749fb directory: cast DEVICE_INARCHIVE, DEVICE_CONTAINER to dev_t
Fix gcc warning.
2010-09-23 09:01:25 +02:00
Max Kellermann
586b7601c6 playlist_database: initialize pm.mtime 2010-09-23 09:01:23 +02:00
Max Kellermann
4425989898 fd_util: work around aliasing warning in recvmsg_cloexec() 2010-09-23 09:01:20 +02:00
Max Kellermann
5b996ab880 output/httpd: access sockaddr_storage object directly
Work around aliasing warning.
2010-09-23 09:01:17 +02:00
Max Kellermann
635cfbae13 decoder_control: use g_free() to manage mixramp allocations
Be consistent with the rest of MPD, and don't use the non-portable
header "malloc.h".
2010-09-23 08:49:21 +02:00
Max Kellermann
922e51e8a9 autogen.sh: enable automake 1.11 2010-09-23 08:37:26 +02:00
Avuton Olrich
6d9f1ad61f configure.ac: Add enable_tremor to post decoder plugin tests. 2010-09-11 20:08:35 -07:00
Avuton Olrich
841b9b3d63 configure.ac: Move use_tremor to enable_tremor. 2010-09-11 20:07:55 -07:00
Avuton Olrich
a3745ae7f3 configure.ac: Correct and clean up tremor check in oggvorbis test. 2010-09-11 20:06:05 -07:00
Avuton Olrich
27d7013ff8 configure.ac: Only enable libogg if OggTremor path/opt has not been specified. 2010-09-11 19:57:44 -07:00
Avuton Olrich
27d3340af2 configure.ac: Fix OggFLAC/tremor test. 2010-09-11 19:28:30 -07:00
Qball Cow
4a7abc9d44 Correctly terminate stream_title.
This caused random data to be send via icy-server if the played
song had no tags.
2010-09-08 13:19:59 +02:00
Max Kellermann
589bb54111 input/curl: fix version check for curl_multi_timeout()
According to the CURL web site, curl_multi_timeout() was added in
version 7.15.4:

 http://curl.haxx.se/libcurl/c/curl_multi_timeout.html
2010-09-07 21:40:56 +02:00
Max Kellermann
d953225531 update_walk: update existing playlist entry
Fixes duplicate playlist entries.
2010-09-07 20:22:05 +02:00
Max Kellermann
663815ead8 playlist_vector: update_or_add() returns bool
False if the vector was not modified.
2010-09-07 20:21:19 +02:00
Anton Khirnov
bc87ec0059 doc/protocol: update descriptions of the searching commands 2010-08-31 06:56:54 +02:00
Max Kellermann
917434269c output/httpd: implement "pause"
Send silence to all connected clients while paused, to avoid
connection interruption.
2010-08-31 06:50:14 +02:00
Max Kellermann
a77506ae21 output/httpd: forced flush after 32 kB of input data
Avoid buffer underruns on the streaming client, if the encoder is "too
efficient" (e.g. when encoding silence while paused).
2010-08-31 06:49:06 +02:00
Johan Kiviniemi
ed5d297301 ReplayGain filter: allow gain > 100 %
The ReplayGain filter clamped the gain to max. 100 % even if the
algorithm determined the signal needed a boost. That would result in any
such tracks being played with too low volume, effectively defeating the
purpose of the filter.
2010-08-23 16:34:11 +03:00
Max Kellermann
64dacd175a output_thread: fix race condition after CANCEL command
Clear the notification before finishing the CANCEL command, so the
notify_wait() after that will always wait for the right notification,
sent by audio_output_all_cancel().
2010-08-19 11:05:24 +02:00
Max Kellermann
625e4755d1 notify: add function notify_clear() 2010-08-19 11:03:53 +02:00
Anton Khirnov
92b6ba9eff doc/protocol: mention that 'status' command also returns 'random' 2010-08-15 11:35:21 +02:00
Max Kellermann
68c02fc95a fd_util: add function dup_cloexec()
Unfortunately, there's no "optimized" implementation here.  We can't
use Linux's proprietary system call dup3(), because it would require
us to specify the new descriptor.
2010-08-03 18:03:55 +02:00
Max Kellermann
d18c1b1a0a fd_util: add function recvmsg_cloexec() 2010-08-03 17:51:35 +02:00
Max Kellermann
c980fc653d fd_util: add function socketpair_cloexec() 2010-08-03 17:51:35 +02:00
Avuton Olrich
36782a977a Modify version string to post-release version 0.16~git 2010-07-25 07:44:45 -07:00
Avuton Olrich
653c4792b5 mpd version 0.16~alpha2 2010-07-25 07:44:44 -07:00
Serge Ziryukin
5cb061ebdf Makefile.am: simplify (de)installation of doxygen-generated documentation
This also fixes installation when no *.gif files found in 'doc/api/html' folder.
2010-07-25 14:04:24 +02:00
Max Kellermann
12f4225d6b Makefile.am: fix out-of-tree doxygen call
This hack creates a copy of doc/doxygen.conf with the INPUT setting
replaced.
2010-07-25 14:02:10 +02:00
Max Kellermann
50862a1dd1 valgrind.suppressions: update g_get_language_names() 2010-07-25 13:38:08 +02:00
Max Kellermann
013ebb638a playlist_song: fix memory leak
Free the temporary path string in apply_song_metadata().
2010-07-25 13:35:59 +02:00
Max Kellermann
bedb82bf4d directory: free playlist vector in directory_free() 2010-07-25 13:31:39 +02:00
Max Kellermann
cbb1ab58cd queue_save: save tags and range of non-database songs
Use the functions song_save() and song_load() to use the same format
as in the database file for those songs which need the tags.
2010-07-25 13:28:54 +02:00
Max Kellermann
b01235e330 song_save: save start_ms and end_ms
While this is not useful for the database, it may become useful for
reusing this code for the state file.
2010-07-25 13:28:52 +02:00
Max Kellermann
8341a9f7b2 queue_save: simplify get_song()
Don't try db_get_song() if the URI has a scheme.
2010-07-25 13:28:48 +02:00
Max Kellermann
b233c145fa {queue,song}_print: print relative paths if possible
If a song with an absolute path points inside the music directory,
print only the relative part.  This happens when partial songs from a
playlist file were loaded.
2010-07-25 13:28:39 +02:00
Max Kellermann
9de5bb9e23 queue_print: hide HTTP password in the "playlist" command
I've already changed the "playlistinfo" command to hide HTTP
passwords, but forgot to do the same for the simpler "playlist"
command.  This patch changes queue_print_uris() to use the code from
song_print_uri().
2010-07-25 13:24:19 +02:00
Max Kellermann
0c5305c51f queue_save: move code to queue_save_song() 2010-07-25 12:43:54 +02:00
Max Kellermann
63c9a20f96 queue_save: queue_load_song() returns void
The only caller doesn't use its return value, and the value isn't
useful anyway.
2010-07-25 12:43:18 +02:00
Max Kellermann
b40c0811f4 song_save: song_load() returns NULL instead of false 2010-07-25 12:23:52 +02:00
Max Kellermann
838790fc2d state_file: use the text_file library
Don't use a large stack buffer.
2010-07-25 12:01:59 +02:00
Max Kellermann
1ff2d5b689 playlist_state: simplify printf() calls
Let the C compiler concatenate string constants.
2010-07-25 12:01:59 +02:00
Max Kellermann
e4b7a113fd database, ...: remove EINTR checks after stdio calls
MPD doesn't have child processes anymore, and thus we're not expecting
to receive SIGCHLD very often.  Since hard disk access isn't
interrupted by signals anyway, we don't need those excessive checks.
2010-07-25 12:01:59 +02:00
Max Kellermann
7820ebb82e directory_save: duplicate the playlist name
The function playlist_metadata_load() will overwrite the input buffer
before using the "name" parameter; since "name" points to the same
buffer, we'll get a corrupted string.
2010-07-25 12:01:59 +02:00
Rasi
31ab0b3df1 Added album tag support for libgme decoder 2010-07-23 23:13:03 +02:00
Max Kellermann
e598922133 update: store playlist files in database
Don't open the music directory for each "lsinfo" call.  Get the list
of playlist files from the memory database.
2010-07-21 09:26:50 +02:00
Max Kellermann
e21ad70f3f song_save: pass const songvec to songvec_save() 2010-07-21 09:26:50 +02:00
Max Kellermann
a81cb932c2 song_save: include cleanup 2010-07-21 09:26:46 +02:00
Max Kellermann
3d66a4fee8 database: remove outdated comment 2010-07-21 09:20:14 +02:00
Avuton Olrich
676739c426 Modify version string to post-release version 0.15.13~git 2010-07-21 06:40:33 +02:00
Max Kellermann
dea5601e79 tag_rva2: move code to rva2_apply_data() 2010-07-20 23:05:24 +02:00
Max Kellermann
43a840552f tag_rva2: add struct rva2_data
.. and several accessor inline functions, to simplify the calling
code.
2010-07-20 23:05:06 +02:00
Max Kellermann
2f16f8e9f7 tag_rva2: name the CHANNEL_* enum 2010-07-20 23:05:01 +02:00
Max Kellermann
4364b30c42 decoder/mad: move RVA2 code to tag_rva2.c 2010-07-20 23:03:54 +02:00
Max Kellermann
c0da938d4f Merge release 0.15.12 from branch 'v0.15.x'
Conflicts:
	NEWS
	configure.ac
2010-07-20 23:03:06 +02:00
Max Kellermann
fb19aa355e replay_gain_info: fix indentation 2010-07-20 22:59:04 +02:00
Max Kellermann
6135419ac3 test/run_decoder: dump replay gain 2010-07-20 22:58:42 +02:00
Max Kellermann
0fec8e0864 mpd version 0.15.12 2010-07-20 22:57:57 +02:00
Max Kellermann
1f976d6e54 input/curl: remove assertion after curl_multi_fdset()
Some users reported that MPD crashes when using a new CURL version
with the threaded DNS resolver enabled.  It seems that
curl_multi_fdset() returns no file descriptor when the DNS resolver
runs in another thread, so MPD does not have any event to wait for.

On the CURL mailing list, somebody suggested to sleep for a fixed
amount of time.  This is not an elegant solution, because daemons
should never have to sleep without waiting for an event.  I hope the
CURL developers will review the API and remove the threaded DNS
resolver.

Meanwhile, I'm removing the assertion in question, to allow those
unfortunate users running the latest CURL version to continue using
MPD.
2010-07-20 22:55:13 +02:00
Max Kellermann
a4908dca42 input/curl: query timeout from CURL
Use curl_multi_timeout() to determine the select() timeout, instead of
hard-coding one second.
2010-07-20 22:37:34 +02:00
Max Kellermann
8b055c3127 tag_rva2: set "gain", not "peak"
RVA2 tags only store the "gain" value, there is no "peak" attribute.
2010-07-20 22:32:55 +02:00
Max Kellermann
172182b18f decoder/mad: parse_rva2() returns bool 2010-07-20 18:27:11 +02:00
Max Kellermann
898a13f196 decoder/wildmidi: support version 0.2.3
In libwildmidi 0.2.3, the function WildMidi_SampledSeek() was removed,
without changing the SO name.  This patch adds an autoconf check for
that function.  Fall back to WildMidi_FastSeek() if
WildMidi_SampledSeek() is not available anymore.
2010-07-20 18:23:56 +02:00
Max Kellermann
2bc5161e95 Merge release 0.15.11 from branch 'v0.15.x
Conflicts:
	NEWS
	configure.ac
	src/decoder/ffmpeg_decoder_plugin.c
	src/decoder/mikmod_decoder_plugin.c
	src/decoder/mp4ff_decoder_plugin.c
	src/decoder/vorbis_decoder_plugin.c
	src/directory_print.c
	src/playlist_control.c
	src/tag_ape.c
2010-07-19 23:49:15 +02:00
Max Kellermann
e2e8d0d2f3 decoder/ffmpeg: fix libavformat 0.6 by using av_open_input_stream()
libavformat 0.6 does not pass the original URI pointer to the "open"
method, which leads to a crash because MPD was using a dirty hack to
pass a pointer to that method.

This patch switches to av_open_input_stream() with a custom
ByteIOContext class, instead of doing the URI string hack with
av_open_input_file().

Loosely based on a patch from Jasper St. Pierre.
2010-07-19 23:03:06 +02:00
Max Kellermann
b2e3c0757b decoder/ffmpeg: manual format probing
Use the libavformat function av_probe_input_format() to probe the
AVInputFormat, instead of letting av_open_input_file() do it
implicitly.  We will switch to av_open_input_stream() very soon, which
does not have the probing code.

Loosely based on a patch from Jasper St. Pierre.
2010-07-19 22:59:59 +02:00
Avuton Olrich
b97e92468f Modify version string to post-release version 0.15.12~git 2010-07-14 17:33:29 -07:00
Avuton Olrich
56bf4ede18 mpd version 0.15.11 2010-07-14 17:33:28 -07:00
Anton Khirnov
0fc0196dba ffmpeg: read the audio stream metadata in addition to global
in some formats, e.g. vorbis, the metadata is stored per-stream.
2010-07-12 11:36:55 +02:00
Anton Khirnov
1492324c76 ffmpeg: factor tag_type <-> ffmpeg tag name maps into a table. 2010-07-12 11:36:55 +02:00
Max Kellermann
49bc317fb8 decoder/ffmpeg: fix libavformat 0.6 by using av_open_input_stream()
libavformat 0.6 does not pass the original URI pointer to the "open"
method, which leads to a crash because MPD was using a dirty hack to
pass a pointer to that method.

This patch switches to av_open_input_stream() with a custom
ByteIOContext class, instead of doing the URI string hack with
av_open_input_file().

Loosely based on a patch from Jasper St. Pierre.
2010-06-30 23:41:32 +02:00
Max Kellermann
375a09d6f6 decoder/ffmpeg: manual format probing
Use the libavformat function av_probe_input_format() to probe the
AVInputFormat, instead of letting av_open_input_file() do it
implicitly.  We will switch to av_open_input_stream() very soon, which
does not have the probing code.

Loosely based on a patch from Jasper St. Pierre.
2010-06-30 23:40:31 +02:00
Max Kellermann
0265c34bed decoder/ffmpeg: free URI, fix memory leak
Free the string allocated by decoder_get_uri().
2010-06-30 23:40:04 +02:00
Max Kellermann
a1882f48be decoder/vorbis: handle uri==NULL
This fixes a theoretical crash, which has never occurred in practice.
2010-06-30 23:38:49 +02:00
Anton Khirnov
c3569814bd ffmpeg: read more metadata. 2010-06-30 23:18:45 +02:00
Max Kellermann
814daac5ba decoder/ffmpeg: free AVFormatContext on error
Fix a memory leak in some code paths.
2010-06-30 23:14:43 +02:00
Max Kellermann
0d03bdce6d configure.ac: check ffmpeg version number with pkg-config
Replace the check for avcodec_decode_audio2(), assume it's present in
libavcodec version 51.
2010-06-30 21:56:04 +02:00
Max Kellermann
768be22f7c pcm_buffer: make the buffer pointer "void" 2010-06-30 21:55:46 +02:00
Max Kellermann
ec89ce5a8a decoder/mp4ff: support tag "album artist"
We already supported "albumartist", but it seems some folks also use
"album artist" (with a space).
2010-06-30 21:55:03 +02:00
Andreas Vögele
34415bf0b6 Make get_remote_uid() work on BSD
I've attached a patch that will make file URIs work on operating systems
that provide the getpeereid() function call to check the user ID of the
peer connected to a UNIX domain socket.
2010-06-30 21:42:01 +02:00
Max Kellermann
0a0c78674f playlist: emit IDLE_OPTIONS when resetting single mode 2010-06-30 21:40:33 +02:00
Max Kellermann
1bffdabe41 directory_print: return void
There is no useful return value here.
2010-06-30 21:39:34 +02:00
Max Kellermann
77e6810c14 decoder/mikmod: fix memory leak
The return value of Player_LoadTitle() is allocated with malloc(), and
must be freed by the caller.
2010-06-30 19:37:36 +00:00
Max Kellermann
5ebe33653c decoder/mp4ff: remove duplicate entries in the tag name table
Reuse the function tag_name_parse_i().
2010-06-30 21:36:15 +02:00
Max Kellermann
8e3eace289 decoder/mp4ff: moved code to mp4ff_tag_name_parse() 2010-06-30 21:36:00 +02:00
Max Kellermann
284659034d tag_ape: remove duplicate entries in the tag name table
Reuse the function tag_name_parse_i().
2010-06-30 21:33:19 +02:00
Max Kellermann
9550c87327 tag: added function tag_name_parse()
Convert a string into a tag_type enum.
2010-06-30 21:31:45 +02:00
Max Kellermann
e223e8a5b5 tag_ape: move code to tag_ape_name_parse() 2010-06-30 21:30:21 +02:00
Max Kellermann
4d6d372a5b decoder/vorbis: use single global ov_callbacks constant
Initialize the ov_callbacks struct at compile time.
2010-06-30 19:24:41 +00:00
Max Kellermann
0aeec90590 decoder/mp4ff: support tags "albumartist", "band"
I'm not sure if mapping "band" to TAG_PERFORMER is correct, but it
might be better than nothing.
2010-06-30 21:22:13 +02:00
Max Kellermann
cfcd84655c decoder/mp4ff: use tag_table.h to parse tag names
Convert if/else/else/... to a loop.
2010-06-30 21:19:30 +02:00
Max Kellermann
5092eaf1cc tag_ape: move table lookup to tag_table.h
Allow code sharing.
2010-06-30 21:18:27 +02:00
Max Kellermann
9328558fc7 tag_ape: support album artist
I took this tag name from a MusePack sample file I got from a user.
It is not documented in the APE specification:

 http://wiki.hydrogenaudio.org/index.php?title=APE_key

People seem to be using undocumented extensions to the specification
anyway, and the best we can do is attempt to support them.
2010-06-30 21:16:28 +02:00
Max Kellermann
026bd15872 tag_ape: simplified the apeItems array
Make "enum tag_type" the array index, and convert apeItems to a sparse
array.
2010-06-30 21:15:52 +02:00
Max Kellermann
7cca55549b tag_ape: moved code to tag_ape_import_item()
Improve code readability.
2010-06-30 21:15:39 +02:00
Max Kellermann
c7e89ea3a3 tag_ape: converted apeItems and tagItems to global vars
Don't initialize those arrays each time tag_ape_load() is called.
2010-06-30 21:15:14 +02:00
Max Kellermann
002b283433 configure.ac: check ffmpeg version number with pkg-config
Replace the check for avcodec_decode_audio2(), assume it's present in
libavcodec version 51.
2010-06-30 10:51:07 +02:00
Max Kellermann
5a2820ca3d pcm_buffer: make the buffer pointer "void" 2010-06-30 08:21:48 +02:00
Max Kellermann
9eb292c1e5 decoder/mp4ff: support tag "album artist"
We already supported "albumartist", but it seems some folks also use
"album artist" (with a space).
2010-06-25 22:54:18 +02:00
Max Kellermann
5216cfb3c8 playlist_print: use playlist_open_any()
Allow printing remote playlists with the commands "listplaylist" and
"listplaylistinfo".
2010-06-25 22:43:52 +02:00
Max Kellermann
373d1843a8 playlist_queue: use playlist_open_any() 2010-06-25 22:43:52 +02:00
Max Kellermann
82ee278f53 playlist_any: new library to simplify opening playlist files
The new function playlist_open_any() combines playlist_mapper_open(),
playlist_list_open_uri() and playlist_list_open_stream(), providing an
easy API for all of them.
2010-06-25 22:43:52 +02:00
Max Kellermann
c27fc26ecd playlist_list: probe plugin without MIME attributes
If the MIME type of a stream contains a semicolon, remove it and the
text after it to probe for a playlist plugin.
2010-06-25 22:43:52 +02:00
Max Kellermann
fba13bd5d2 playlist_queue: simplify error handler
Return early on error, save one level of indent.
2010-06-25 22:37:34 +02:00
Max Kellermann
68f75955d8 playlist_list: moved input_stream.ready loop to calling function
Merged both loops into playlist_list_open_stream().  This is needed
because playlist_list_open_stream() needs to know the MIME type, which
is only known after the stream has become "ready".
2010-06-25 22:37:34 +02:00
Max Kellermann
cd21cfc115 uri: really count dots in verify_uri_segment()
This buggy implementation failed to allow "..." sequences, because the
dot count was always zero.  The usefulness of allowing "..." (or more
dots) is debatable, but since it's a valid file name, we allow it.
2010-06-25 21:56:12 +02:00
Max Kellermann
05703cf73b update .gitignore 2010-06-25 21:46:16 +02:00
Max Kellermann
1ea10db953 playlist/cue: last track ends at end of file
libcue's track_get_length() returns 0 for the last track, because that
information is not available in the CUE sheet.  This makes MPD quit
playing the last track immediately.  If we set "song.end_ms=0", MPD
will play the track until the end of the song file, which is what we
want.
2010-06-25 20:02:55 +02:00
Max Kellermann
eb6188f8c0 test/dump_playlist: print open-ended range 2010-06-25 20:02:55 +02:00
Max Kellermann
6bb7a7439d song_print: simplified start_ms/end_ms check 2010-06-25 20:02:55 +02:00
Aaron Griffith
ca6110d92f playlist_list: wait for input stream to become ready
Fixes an assertion failure in the input_stream_seek() call.
2010-06-25 19:20:20 +02:00
Avuton Olrich
a219d488d0 Modify version string to post-release version 0.16~git 2010-06-22 04:45:24 -07:00
Avuton Olrich
1746257492 mpd version 0.16~alpha1 2010-06-22 04:45:24 -07:00
Max Kellermann
07423c300e Makefile.am: append LIBWRAP_LDFLAGS to OUTPUT_LIBS
This fixes test/run_output.
2010-06-21 09:45:35 +02:00
Andreas Vögele
69fcdc6a07 autogen.sh: don't use "+" in sed expression
Replaces the expression [0-9]\+ by [0-9][0-9]* in autogen.sh. The
special character \+ is not POSIX compatible and thus not supported by
all sed implementations.
2010-06-19 13:49:20 +02:00
Andreas Vögele
dda5415def Make get_remote_uid() work on BSD
I've attached a patch that will make file URIs work on operating systems
that provide the getpeereid() function call to check the user ID of the
peer connected to a UNIX domain socket.
2010-06-19 13:47:10 +02:00
Andrzej Rybczak
4347114455 client: increase send_buf from 4kB to 16kB
this greatly improves performance of commands that return a lot
of data, e.g. search results or recursive content of a directory,
while being connected to local mpd via tcp/ip socket.
2010-06-19 13:45:54 +02:00
Max Kellermann
9fb26b5617 playlist: check "queued" before calling playlist_update_queued_song()
Workaround for an assertion failure.
2010-06-19 13:45:15 +02:00
Max Kellermann
442b327582 playlist: protect acess to player state in playlist_sync() 2010-06-19 13:33:39 +02:00
Max Kellermann
cff727644d playlist: move checks out of playlist_sync_with_queue()
Rename the function to playlist_song_started(), which gets only called
if the song has actually started.
2010-06-19 13:33:32 +02:00
Max Kellermann
7f80349494 player_control: hold mutex in pc_play(), pc_pause()
Race condition fix.
2010-06-19 13:19:30 +02:00
Avuton Olrich
a6ef696132 Makefile.am: distribute refcount.h 2010-06-13 19:49:38 -07:00
Max Kellermann
53f08a95eb Makefile.am: distribute open.h 2010-06-08 21:40:51 +02:00
Max Kellermann
d0d2d6ed1b filter/route: fixed unsigned/signed comparison
Fixes a clang warning.
2010-06-06 21:56:30 +02:00
Tim Phipps
4e83b79d2b output/httpd: add libwrap support
libwrap support is in MPD but only for the control port.  This patch
adds support for the http port.  The code is copied from
src/client_new.c
2010-06-06 21:53:24 +02:00
Max Kellermann
37e69a89b9 directory_print: print a list of playlist files in "lsinfo"
Print those files which can be handled by a playlist plugin.
2010-06-01 10:00:01 +02:00
Max Kellermann
6f28eb4c0a playlist_list: export playlist_suffix_supported() 2010-06-01 09:59:56 +02:00
Max Kellermann
caf93d9a2c playlist_list: playlist_list_open_path() returns input_stream
Memory leak fix.  The input_stream object passed to
playlist_list_open_stream_suffix() must be closed by the caller - this
however never happens in playlist_list_open_path(), because it does
not return it to the caller.
2010-06-01 09:59:54 +02:00
Max Kellermann
747e945d29 playlist_list: move fallback g_uri_parse_scheme() to glib_compat.h 2010-06-01 09:59:52 +02:00
Max Kellermann
067d7212bf playlist: emit IDLE_OPTIONS when resetting single mode 2010-06-01 09:59:46 +02:00
Romain Bignon
867b93a5b2 single mode: at the end of the song, pause on the next song instead of stop 2010-05-31 23:36:16 +02:00
Max Kellermann
53647c8ba1 directory_print: return void
There is no useful return value here.
2010-05-31 22:16:03 +02:00
Romain Bignon
b571ba27a4 fix crash when decoder is NULL 2010-05-31 22:11:59 +02:00
Max Kellermann
de8f2739c2 decoder/mikmod: fix memory leak
The return value of Player_LoadTitle() is allocated with malloc(), and
must be freed by the caller.
2010-05-31 10:24:08 +02:00
Max Kellermann
2c1c588868 decoder/mikmod: duplicate the path only once 2010-05-31 10:22:08 +02:00
Max Kellermann
e271f69a34 decoder/mikdmod: moved local variable declarations in tag_dup() 2010-05-31 10:16:09 +02:00
Max Kellermann
39105f2119 decoder/wildmidi: moved local variable declarations in tag_dup() 2010-05-31 10:14:48 +02:00
Max Kellermann
4e080a9b06 decoder/gme: replace macro GME_BUF_SIZE
Added macros with better names: GME_BUFFER_FRAMES, GME_BUFFER_SAMPLES
(the former only used to calculate the latter).
2010-05-31 10:03:43 +02:00
Max Kellermann
d55740808d decoder/gme: added constants for sample rate and channels
Move into enums.
2010-05-31 10:02:43 +02:00
Max Kellermann
6566038df9 decoder/gme: pass the whole buffer to decoder_data()
Pass sizeof(buf) to decoder_data(), not the number of samples (which
is half the size).  At the same time, pass GME_BUF_SIZE to gme_play()
- libgme really wants to get the number of samples, not the number of
stereo frames.  Previously, this plugin had been using only the first
half of the buffer.
2010-05-31 09:57:15 +02:00
Max Kellermann
f0faf00320 decoder/gme: allocate tag later, fixes memory leak
Allocate the "tag" object after the file has been opened successfully.
2010-05-31 09:43:25 +02:00
Max Kellermann
51c7577c8f decoder/mp4ff: rename and move local variable
Allocate the "tag" object after the file has been checked.  That
removes one tag_free() call in an error handler.
2010-05-31 09:35:49 +02:00
Max Kellermann
be308c6657 decoder/mp4ff: merge code into mp4ff_input_stream_open() 2010-05-31 09:29:45 +02:00
Max Kellermann
1729a0b437 decoder/mp4ff: move mp4ff_callback_t into mp4ff_input_stream
Allocate only one item on the stack.
2010-05-31 09:23:27 +02:00
Max Kellermann
1b2fc2ae19 decoder/mp4ff: rename mp4_context to mp4ff_input_stream 2010-05-31 09:22:42 +02:00
Max Kellermann
58a75f0b78 decoder/mp4ff: remove duplicate entries in the tag name table
Reuse the function tag_name_parse_i().
2010-05-31 08:48:41 +02:00
Max Kellermann
daf6e2b5da decoder/mp4ff: moved code to mp4ff_tag_name_parse() 2010-05-31 08:47:59 +02:00
Max Kellermann
71da5b026c tag_ape: remove duplicate entries in the tag name table
Reuse the function tag_name_parse_i().
2010-05-31 08:24:05 +02:00
Max Kellermann
2f68ea635b tag_ape: move code to tag_ape_name_parse() 2010-05-31 08:22:53 +02:00
Max Kellermann
ca08c4dbf4 decoder/vorbis: rename local variable "ret" 2010-05-31 00:06:37 +02:00
Max Kellermann
8af52fa205 decoder/vorbis: implement method stream_tag()
This allows tags in archive files.
2010-05-31 00:02:57 +02:00
Max Kellermann
3b0bf856c4 decoder/vorbis: move code to vorbis_open_stream() 2010-05-31 00:00:04 +02:00
Max Kellermann
28f5803d34 decoder/vorbis: use single global ov_callbacks constant
Initialize the ov_callbacks struct at compile time.
2010-05-30 23:59:36 +02:00
Max Kellermann
0b72299857 decoder/vorbis: rename struct vorbis_decoder_data to vorbis_is 2010-05-30 23:57:58 +02:00
Max Kellermann
4a2302c2ed inotify_update: follow symlinks
This is probably unsafe, and doesn't protect against symlink loops,
but we will eventually add this when we bring update*.c and inotify*.c
closer together.
2010-05-30 23:45:47 +02:00
Max Kellermann
980201a665 inotify_update: fix assertion failure when music dir is deleted
This shouldn't really happen, but insane users might delete/rename the
music directory while MPD runs.  What was even more insane was that
MPD crashed due to this.  This is a workaround - there is currently
nothing useful we can do in this case; except maybe poll for the music
directory to reappear, but that's too much trouble for a user error.
2010-05-30 23:45:46 +02:00
Max Kellermann
d60bcd2869 test: added debug program "run_inotify" 2010-05-30 23:30:43 +02:00
Max Kellermann
017d61a1aa decoder/mp4ff: support tags "albumartist", "band"
I'm not sure if mapping "band" to TAG_PERFORMER is correct, but it
might be better than nothing.
2010-05-30 22:55:02 +02:00
Max Kellermann
4028d03a6e decoder/mp4ff: use tag_table.h to parse tag names
Convert if/else/else/... to a loop.
2010-05-30 22:52:45 +02:00
Max Kellermann
44b771aca1 decoder/mp4ff: rename plugin "mp4" to "mp4ff"
The underlying library is named "libmp4ff".  To reduce confusion,
rename the plugin to a more specific name.
2010-05-30 22:52:44 +02:00
Max Kellermann
8c581eca4d tag_ape: move table lookup to tag_table.h
Allow code sharing.
2010-05-30 22:52:19 +02:00
Max Kellermann
8e815bec9f tag_ape: support album artist, MusicBrainz tags
I took these tag names from a MusePack sample file I got from a user.
These are not documented in the APE specification:

 http://wiki.hydrogenaudio.org/index.php?title=APE_key

People seem to be using undocumented extensions to the specification
anyway, and the best we can do is attempt to support them.
2010-05-30 22:29:48 +02:00
Max Kellermann
9dda53e1d2 Merge release 0.15.10 from branch 'v0.15.x'
Conflicts:
	NEWS
	configure.ac
	src/input/mms_input_plugin.c
2010-05-30 18:26:33 +02:00
Avuton Olrich
65ad298460 Modify version string to post-release version 0.15.11~git 2010-05-30 08:59:00 -07:00
Avuton Olrich
57e95ea6f4 mpd version 0.15.10 2010-05-30 08:59:00 -07:00
Max Kellermann
442d2e74e3 decoder/mad: fix buffer variable name on !HAVE_ID3TAG 2010-05-30 17:27:03 +02:00
Daniel Seuthe
e8310211e2 filter/replay_gain: added option "replaygain_limit" 2010-05-30 17:06:08 +02:00
Avuton Olrich
3709b9aa11 configure: Surround statements with brackets to prevent being treated as args. 2010-05-26 12:38:03 -07:00
Avuton Olrich
c5a291ad74 configure.ac: Add sqlite results to the pretty print. 2010-05-25 22:01:28 -07:00
Avuton Olrich
894573657d cmdline: Add default config paths for win32. 2010-05-20 06:56:43 -07:00
Max Kellermann
9cce1d749a output/win32: new output plugin for Windows Wave 2010-05-20 09:33:05 +02:00
Max Kellermann
ed0b48040c pcm_buffer: 8 kB alignment instead of 64 kB
Reduce the overhead.  Most buffers used by MPD are around 2 to 4 kB.
8 kB seems large enough to keep heap fragmentation low.

Additionally, this patch fixes an off-by-one error in the alignment
formula.
2010-05-20 09:31:51 +02:00
Max Kellermann
9aeed06964 test: enable binary mode on stdin/stdout
Declaring the global variable _CRT_fmode is a documented hack.  What
an ugly platform!
2010-05-20 09:20:30 +02:00
Max Kellermann
eda46bc07f tag_{id3,ape}: explicitly open files in binary mode
Add the flag "b" to fopen().  Windows compatibility.
2010-05-20 09:14:11 +02:00
Max Kellermann
c727e86980 input/file, output/{fifo,recorder}: add O_BINARY to open() flags
Windows compatibility.
2010-05-20 09:13:39 +02:00
Max Kellermann
57b4013306 client: use sprintf() on WIN32
On mingw32, snprintf() expects a 64 bit integer instead of a "long
int" for "%li" - this is not consistent with our expectation, so we're
using plain sprintf().
2010-05-19 13:12:37 +02:00
Max Kellermann
fc5a7a8774 Makefile.am: disable test/read_mixer unless HAVE_ALSA 2010-05-19 11:47:55 +02:00
Max Kellermann
8248f498b2 event_pipe: use g_io_channel_read_chars() to drain the pipe
For some unknown reason, read() blocks on WIN32, even though it was
invoked inside the G_IO_IN callback.  By switching to GIOChannel
functions, this problem is solved, and it works on both Linux and
Windows.
2010-05-19 11:37:39 +02:00
Max Kellermann
22f3ffb099 event_pipe: call g_io_channel_win32_new_fd()
On WIN32, use g_io_channel_win32_new_fd() instead of
g_io_channel_unix_new().  There doesn't seem to be a practical
difference, but it seems more correct.
2010-05-19 11:37:22 +02:00
Max Kellermann
81bd8ef73c update_walk: don't check recursive symlinks on WIN32
inode numbers don't work on WIN32, and very few WIN32 users actually
use symlinks - seems ok to disable that check.
2010-05-19 11:36:07 +02:00
Max Kellermann
bedc172eab crossfade: emulate strtok_r() on WIN32 with a function
Not a CPP macro.  This adds the "unused" attribute to "saveptr", which
eliminates one gcc warning.
2010-05-18 23:56:53 +02:00
Max Kellermann
0a9b0dd070 command: fallback strftime() formats on WIN32
On WIN32, "%F" and "%T" are not implemented.
2010-05-18 23:54:12 +02:00
Max Kellermann
6aa0f61e15 socket_util: fix setsockopt() argument type on WIN32
In the winsock headers, the setsockopt() argument is declared as
"const char *", not "const void *".
2010-05-18 23:13:05 +02:00
Max Kellermann
4461c3d5d1 daemon: include glib.h on WIN32 for g_error() 2010-05-18 23:13:05 +02:00
Max Kellermann
fef46bcf49 fd_util: fd_set_cloexec() returns a value on WIN32
This function is empty on WIN32, but we need to return a value.
2010-05-18 23:09:07 +02:00
Max Kellermann
fa2ff849c5 input/ffmpeg: new input plugin using libavformat's "avio" library 2010-05-18 21:20:31 +02:00
Max Kellermann
6b2b91ff01 input/mms: removed empty method buffer()
input_stream_buffer() has a check for "buffer==NULL", so we don't need
an explicit empty implementation.
2010-05-18 21:20:31 +02:00
Max Kellermann
28736414a8 input/mms: initialize the "eof" attribute 2010-05-18 21:11:00 +02:00
Max Kellermann
e98bd55cbf input/mms: fix memory leak in error handler 2010-05-18 20:57:57 +02:00
Daniel Seuthe
733962db2f decoder_api: return effective replay gain
Pay attention to the ReplayGain mode "auto" and the
replay_gain_missing_preamp parameter inside of decoder_replay_gain().
2010-05-18 20:39:32 +02:00
Daniel Seuthe
f824ecdfa1 replay_gain_config: added function replay_gain_get_real_mode() 2010-05-18 20:30:03 +02:00
Max Kellermann
6c831e04a2 output/oss: 24 bit support via OSS4
Added #ifdefs on the macros AFMT_S24_NE and other OSS4 extensions.
2010-05-14 09:48:02 +02:00
Max Kellermann
67c48f66a6 output/oss: rewrite of the audio format detection
Removed the "supported" / "unsupported" arrays, and auto-detect on
every open() call, just like the ALSA output plugin.
2010-05-13 14:42:09 +02:00
Max Kellermann
5fa1c703de output/oss: use the *_NE macros
Removed the macro AFMT_S16_MPD.
2010-05-13 14:34:22 +02:00
Avuton Olrich
bead892e21 AudioCompress: include stdint.h for int16_t definition.
In mingw32, int16_t is not defined by sys/types.h, but it is by stdint.h,
and it is in the int16_t man page as being defined in stdint.h. Thanks to
mithi for help debugging.
2010-05-11 18:48:22 -07:00
Avuton Olrich
8341daca4a client_new: Send greeting using send() rather than write().
This is portable for win32 and fixes client logins there.
2010-05-08 16:48:01 -07:00
Tim Phipps
136759a298 mixramp: Remove maths from pcm_mix when using MixRamp overlaps. 2010-05-08 09:28:06 +02:00
Tim Phipps
0ac5b6e613 mixramp: Adjust MixRamp threshold to account for ReplayGain. 2010-05-08 00:19:44 -07:00
Avuton Olrich
eb5208c4f9 log: /dev/null doesn't exist on win32. 2010-05-08 00:17:34 -07:00
Max Kellermann
d88c3c8462 output_thread: call replay gain filter manually
Don't add it to the filter chain, because we need to apply replay gain
before cross-fading with the next song.  Add a second replay_gain
filter which is used for the song being faded in (chunk->other).
2010-05-02 18:21:16 +02:00
Max Kellermann
5399a72ec1 player_thread: move cross-fading to output thread
Remove cross_fade_apply(), and call pcm_mix() in the output thread,
mixing the chunk and chunk->other together.
2010-05-02 17:46:07 +02:00
Max Kellermann
d093fb2441 chunk: added attribute "other"
Preparation for cross-fading support in the output thread.
2010-05-02 17:46:06 +02:00
Max Kellermann
0140804ef9 output_thread: moved code to ao_filter_open(), ao_filter_close() 2010-05-02 17:38:16 +02:00
Max Kellermann
ecb5f68ba8 output_thread: moved filter invocation to ao_filter_chunk() 2010-05-02 17:34:35 +02:00
Max Kellermann
55bf242635 filter/replay_gain: removed duplicate *dest_size_r assignment 2010-05-02 17:34:27 +02:00
Avuton Olrich
7dba410c26 configure.ac: Move the encoders before the audio outputs.
Move encoder before audio outputs. Makes sense to keep them after
decoders and some audio outputs need to know wheather encoders are
present.
2010-04-26 06:49:12 -07:00
Daniel Seuthe
92e66e5ab2 replay_gain: added mode "auto" 2010-04-25 13:51:58 +02:00
Max Kellermann
54dedef9cf configure.ac: removed stray comma 2010-04-25 13:51:54 +02:00
Avuton Olrich
b8b2ef3de1 configure.ac: Add libogg section
Add libogg section rather than checking for it every time it's necessary.
2010-04-23 06:53:26 -07:00
Avuton Olrich
681128def4 configure.ac: Fix an omission during moving oggvorbis to decoder plugins. 2010-04-22 20:00:25 -07:00
Avuton Olrich
f4f4a908bb configure.ac: When compiling FLAC with ogg API, linking with ogg is mandatory. 2010-04-22 19:56:23 -07:00
Avuton Olrich
7c37f2879b configure.ac: Move LAME encoder subheader to correct spot. 2010-04-22 19:09:06 -07:00
Avuton Olrich
615ccd1070 configure.ac: Add optional third variable to results.
Add optional third value to results() to allow for non "yes" answers which are
considered positive.
2010-04-21 12:23:38 -07:00
Avuton Olrich
6ed69e6395 libOggFLAC.m4: Remove libOggFLAC.m4 as it's buggy. 2010-04-21 12:23:38 -07:00
Avuton Olrich
f946a182a3 configure.ac: Rewrite the liboggflac tests.
Rewrite the liboggflac tests, the m4 macro is way too complicated for no
reason and it's buggy.
2010-04-21 12:23:38 -07:00
Avuton Olrich
d3fcb81da0 configure.ac: Fix a couple of syntax errors created by the moves. 2010-04-21 12:23:37 -07:00
Avuton Olrich
7e42637b74 configure.ac: Clean up the wording and lines at the end of the file. 2010-04-21 12:23:37 -07:00
Avuton Olrich
dbe93d4a6d configure.ac: Merge the streaming and nonstreaming sections. 2010-04-21 12:23:37 -07:00
Avuton Olrich
20bc25cdcc configure.ac: Move audio output tests to Post Audio Output Plugins section. 2010-04-21 12:23:37 -07:00
Avuton Olrich
13c0788334 configure.ac: Move protocol tests to the Post Protocol Tests section. 2010-04-21 12:23:37 -07:00
Avuton Olrich
672cf84a32 configure.ac: Remove errant header. 2010-04-21 12:23:37 -07:00
Avuton Olrich
2834f7802f configure.ac: Move decoder minimum test to Post Decoder Plugins tests. 2010-04-21 12:23:37 -07:00
Avuton Olrich
b135483abc configure.ac: New pretty print results menu.
This commit adds a new pretty print menu, we've (far) outgrown the old
menu which because hard to traverse.
2010-04-21 12:23:37 -07:00
Avuton Olrich
ba36cbde06 configure.ac: Add a comment in libsndfile about possible disabling. 2010-04-21 12:23:37 -07:00
Avuton Olrich
f153681522 configure.ac: Move AC_DEFINE for Ogg Tremor under other if conditional.
Ogg Tremor doesn't have the ability to be disabled under the first if
conditional, might as well AC_DEFINE there.
2010-04-21 07:35:33 -07:00
Avuton Olrich
d73291959b configure.ac: Organize all added CFLAGs. 2010-04-21 07:35:32 -07:00
Avuton Olrich
01a675eb5f configure.ac: Documentation whitespace fixes. 2010-04-21 07:35:32 -07:00
Avuton Olrich
be5143c0f3 configure.ac: Move SHOUTcast output to Streaming Audio Plugins. 2010-04-21 07:35:32 -07:00
Avuton Olrich
b7ee1f5c40 configure.ac: Move Recorder output to Streaming Audio Plugins. 2010-04-21 07:35:32 -07:00
Avuton Olrich
b619f5e73d configure.ac: Move HTTPD output to Streaming Audio Plugins. 2010-04-21 07:35:32 -07:00
Avuton Olrich
e44f639002 configure.ac: Move final encoder tests to the end of the encoder tests. 2010-04-21 07:35:32 -07:00
Avuton Olrich
1aa470e0bf configure.ac: Move WAVE Encoder to Encoder Plugins. 2010-04-21 07:35:32 -07:00
Avuton Olrich
b6a33691ff configure.ac: Move TwoLAME Encoder to Encoder Plugins. 2010-04-21 07:35:32 -07:00
Avuton Olrich
5e616b4ff3 configure.ac: Move LAME Encoder to Encoder Plugins. 2010-04-21 07:35:31 -07:00
Avuton Olrich
d847593a8e configure.ac: Move OggVorbis Encoder to Encoder Plugins. 2010-04-21 07:35:31 -07:00
Avuton Olrich
1cd5700c37 configure.ac: Move FLAC Encoder to Encoder Plugins. 2010-04-21 07:35:31 -07:00
Avuton Olrich
957cf3c7b0 configure.ac: Add encoder/encoder API header 2010-04-21 07:35:31 -07:00
Avuton Olrich
b6c8de6e01 configure.ac: Move tremor/oggflac test under oggflac. 2010-04-21 07:35:31 -07:00
Avuton Olrich
21f5ed4a25 configure.ac: Check for vorbis once, and AC_DEFINE it there. 2010-04-21 07:35:31 -07:00
Avuton Olrich
52fc168905 configure.ac: Add Post Decoder Plugins Tests header
Add Post Decoder Plugins Tests and move things that shouldn't be in individual
tests to it.
2010-04-21 07:35:31 -07:00
Avuton Olrich
3bb03db097 configure.ac: Move ffmpeg to Decoder Plugins, add header. 2010-04-21 07:35:30 -07:00
Avuton Olrich
ee88b8f8c4 configure.ac: Move WildMidi to Decoder Plugins, add header. 2010-04-21 07:35:30 -07:00
Avuton Olrich
7bfcb6151d configure.ac: Move wavpack to Decoder Plugins, add header. 2010-04-21 07:35:30 -07:00
Avuton Olrich
3c549c5147 configure.ac: Move sidplay to Decoder Plugins, add header. 2010-04-21 07:35:30 -07:00
Avuton Olrich
8b0fb1cc4d configure.ac: Move OggVorbis to Decoder Plugins, add header. 2010-04-21 07:34:55 -07:00
Avuton Olrich
e3d62305f1 configure.ac: Move OggFLAC to Decoder Plugins, add header. 2010-04-21 07:34:55 -07:00
Avuton Olrich
263a968c2b configure.ac: Move Ogg Tremor to Decoder Plugins, add header. 2010-04-21 07:34:54 -07:00
Avuton Olrich
39b37d547c configure.ac: Move musepack to Decoder Plugins, add header. 2010-04-21 07:11:49 -07:00
Avuton Olrich
4cfcdd5e0f configure.ac: Move libsndfile to Decoder Plugins, add header. 2010-04-21 07:11:49 -07:00
Avuton Olrich
9d72a34109 configure.ac: Move sndfile/modplug conflict test to Decoder Plugins, add header. 2010-04-21 07:10:59 -07:00
Avuton Olrich
18cdbfa70f configure.ac: Move libmodplug to Decoder Plugins, add header. 2010-04-21 07:10:16 -07:00
Avuton Olrich
7886fd711f configure.ac: Move libmikmod to Decoder Plugins, add header. 2010-04-21 07:10:16 -07:00
Avuton Olrich
3993ed6b81 configure.ac: Move libmpg123 to Decoder Plugins, add header. 2010-04-21 07:10:15 -07:00
Avuton Olrich
abf974c5c5 configure.ac: Move libmad to Decoder Plugins, add header. 2010-04-21 07:10:15 -07:00
Avuton Olrich
97f80e04b0 configure.ac: Move FluidSynth to Decoder Plugins, add header. 2010-04-21 07:10:15 -07:00
Avuton Olrich
944057c30b configure.ac: Move FLAC to Decoder Plugins, add header. 2010-04-21 07:10:15 -07:00
Avuton Olrich
9c4d4bd6b0 configure.ac: Move FAAD to Decoder Plugins, add header. 2010-04-21 07:10:15 -07:00
Avuton Olrich
a77fd83b15 configure.ac: Move audiofile to Decoder Plugins, add header. 2010-04-21 07:10:15 -07:00
Avuton Olrich
ed78955f8d configure.ac: Move Solaris to Audio Output Plugins (nonstreaming), add header. 2010-04-21 07:10:15 -07:00
Avuton Olrich
cab16f8e1e configure.ac: Move MVP to Audio Output Plugins (nonstreaming), add header. 2010-04-21 07:10:14 -07:00
Avuton Olrich
24db28072f configure.ac: Add header for Pipe Output and Pulseaudio. 2010-04-21 07:10:14 -07:00
Avuton Olrich
e20d3d49b3 configure.ac: Move OSX to Audio Output Plugins (nonstreaming), add header. 2010-04-21 07:10:14 -07:00
Avuton Olrich
33641df09c configure.ac: Move OSS to Audio Output Plugins (nonstreaming), add header. 2010-04-21 07:10:14 -07:00
Avuton Olrich
294cce2710 configure.ac: Move OpenAL to Audio Output Plugins (nonstreaming), add header. 2010-04-21 07:10:14 -07:00
Avuton Olrich
9fb31aafb3 configure.ac: Move libao to Audio Output Plugins (nonstreaming), add header. 2010-04-21 07:10:14 -07:00
Avuton Olrich
791ddc566a configure.ac: Move JACK to Audio Output Plugins (nonstreaming), add subheader. 2010-04-21 07:10:14 -07:00
Avuton Olrich
7d2b2e371c configure.ac: Move FIFO to Audio Output Plugins (nonstreaming), add subheader. 2010-04-21 07:10:13 -07:00
Avuton Olrich
c3ecb5aa5e configure.ac: Move ALSA to Audio Output Plugins (nonstreaming), add subheader. 2010-04-21 07:10:13 -07:00
Avuton Olrich
191e520999 configure.ac: Move converter plugins up with other aux plugins. 2010-04-21 07:10:13 -07:00
Avuton Olrich
dd5fdbc540 configure.ac: Move archive plugins around (alphabetize), add subheaders. 2010-04-21 07:10:13 -07:00
Avuton Olrich
b39855731c configure.ac: Add subheaders to Input/Sticker/Autodiscovery/Metadata and Misc. 2010-04-21 07:10:13 -07:00
Avuton Olrich
e27dd7280f configure.ac: Move libid3tag to "Metadata Plugins" 2010-04-21 07:10:13 -07:00
Avuton Olrich
a9ff134e9f configure.ac: Move inotify to "Miscellaneous Libraries". 2010-04-21 07:10:13 -07:00
Avuton Olrich
11fbb1c807 configure.ac: Move all AC_ARG* functions to a single place in the configure.ac. 2010-04-21 07:10:12 -07:00
Avuton Olrich
15998c0db9 configure.ac: Add better headers to improve readability.
This changes no code, makes no moves; this merely adds some headers
to improve readability.
2010-04-21 07:08:16 -07:00
Max Kellermann
cf3ada3d04 Merge branch 'master' of git://github.com/mcfiredrill/mpd 2010-04-20 21:16:39 +02:00
Avuton Olrich
7df94b1718 lame.m4: Remove unused macro file 2010-04-15 20:23:53 -07:00
Avuton Olrich
c2d788f6de configure.ac: Check for LAME libraries the same way others are checked for.
Check for LAME libraries the same way other libraries are checked for, in line
with the configure and outside the buggy macro. This will fix problems with
cross compilation.
2010-04-15 20:23:35 -07:00
Avuton Olrich
fa8992fc63 configure.ac: Don't allow UNIX IPC to be configured for a native Windows build. 2010-04-13 19:09:26 -07:00
Max Kellermann
8d631b346a inotify_update: start update when directory is created
This is useful at the maximum depth level, to update newly created
directories.  It is however questionable if the hard-coded 5 seconds
delay is enough to create new directory trees with all of their files,
but we might make that delay configurable in the future.
2010-04-13 23:37:44 +02:00
Max Kellermann
393bcd961a inotify: added setting "auto_update_depth"
Limits the depth of the watched directories.  This is useful to keep
resource usage down and speed up MPD startup.
2010-04-13 21:31:57 +02:00
Max Kellermann
4d1eedbaa2 conf: added function config_get_unsigned() 2010-04-13 21:19:07 +02:00
Max Kellermann
a1a03deed2 decoder/mad: properly calculate ID3 size without libid3tag
Without libid3tag, we were trying to skip the ID3 frame (since
0.15.2).  Its length however was not calculated at all, we were just
dropping everything from the current input buffer.  This lead to the
first few seconds of the file being skipped.  This patch attempts to
calculate the ID3v2 frame size with the formula from:

 http://www.id3.org/id3v2.4.0-structure 3.1 and 6.2
2010-04-13 08:51:29 +02:00
Charles Kerr
6a95898038 decoder/ffmpeg: fix crash on startup in mpd_ffmpeg_log_callback()
What's happening is the `ptr' argument to that function is NULL for me
every time. `ptr' is unconditionally dereferenced to generate a log
message, and this is where mpd crashes.

Attached is a simple patch that tests for NULL and omits the log. With
this patch the crash disappeared and mpd went back to working well.
2010-04-11 19:18:30 +02:00
Tony Miller
9d55b16998 Decoder for game music emulation library.
Supports a number of videogame music formats, more info here:
http://www.fly.net/~ant/libs/audio.html

I wrote this plugin for the latest svn, get it here:
http://code.google.com/p/game-music-emu/source/checkout
2010-04-10 01:05:16 -07:00
James Pike
c52f469c9c output/httpd: added name/genre/website configuration 2010-04-05 13:11:10 +02:00
Orivej Desh
50c0c4b701 cue: fix code style 2010-04-01 08:12:25 +02:00
Max Kellermann
8446b70ddc decoder/ffmpeg: print item name in log callback 2010-03-28 19:48:57 +02:00
Orivej Desh
635791d1cd cue: prepend pregap to the beginning of the track
.. rather then append to the end of the previous one

Cuebreakpoints from the cuetools package has three modes of operation,
and the default is to append pregap (INDEX 00) to the end of the
previous track. This is the behavior most compliant to the existing
cue files.

Here is the patch which fixes the issue. I borrowed bits of
implementation from cuebreakpoints. I assumed that the whole audio
file must be covered by head-to-head going tracks, which is how
hardware CD players probably work. In cue_tag I changed rounding from
rounding up to rounding down because the thing in mpd which calculates
actual track duration (and current position) rounds it down, and I
didn't want to see in my playlist values different from whose in a
now-playing progress bar.

I've compared the resultant mpd behaviour with "mplayer -ss MM:SS.MS"
where the time was supplied by cuebreakpoints and noticed that mplayer
started each track a bit earlier then mpd, though this was the same
before the patch.
2010-03-28 19:34:16 +02:00
Max Kellermann
e9beea072d Merge release 0.15.9 from branch 'v0.15.x'
Conflicts:
	NEWS
	configure.ac
	src/cue/cue_tag.c
	src/decoder/mpcdec_decoder_plugin.c
	src/player_thread.c
2010-03-28 19:31:47 +02:00
Max Kellermann
e69bb3b337 Makefile.am: distribute pcm_pack.h
Fix distcheck.
2010-03-28 19:31:02 +02:00
Avuton Olrich
06f86b4532 Add support for building httpd_output plugin for win32 2010-03-22 07:15:39 -07:00
Avuton Olrich
68ece2fef3 Define winnt or greater on mingw32 to take advantage of get*info(). 2010-03-21 18:45:32 -07:00
Avuton Olrich
4953ea90c2 strtok() is recursive by default on win32. 2010-03-21 18:24:24 -07:00
Avuton Olrich
0dcd865c2e Modify version string to post-release version 0.15.10~git 2010-03-21 17:25:18 -07:00
Tim Phipps
e7a515c8b1 Add support for MixRamp tags
Adds mixrampdb and mixrampdelay commands.  Reads MIXRAP_START and
MIXRAMP_END tags from FLAC files and overlaps instead of crossfading.
2010-03-21 18:21:47 +01:00
Max Kellermann
e9b75d462c AudioCompress: explicitly include stdint.h
For int16_t.
2010-03-17 18:29:56 +01:00
Max Kellermann
68017b1254 decoder_api: use the correct format to calculate elapsed time
When decoder->timestamp is calculated, the PCM data is already
converted to out_audio_format; using in_audio_format may cause funny
speedups/slowdowns.
2010-03-17 18:26:36 +01:00
Max Kellermann
469c9b5def command: allow "update" with slash or empty path
When handle_update() was modified to use uri_safe_local(), suddently
"mpc update ''" and "mpc update '/'" stopped working, because both are
not a "safe" local URI.  This patch adds a special case for these, to
retain backwards compatibility.
2010-03-12 17:58:16 +01:00
Max Kellermann
e686d19154 output: added option "always_on" for radio stations
Did you ever accidently click "stop" while feeding a radio station?
This option sets the output device to "pause" to disable the "close"
method.  It falls back to "pause" then, which is specific to the
plugin.  Some plugins implement it by feeding silence.
2010-03-10 19:49:31 +01:00
Max Kellermann
762565e9d1 output/jack: drain the ring buffers during pause
If we're not doing this, and a new song is played after pause ends,
then you will hear the rest of the previous song.
2010-03-10 19:48:49 +01:00
Max Kellermann
41a4662c8c output/jack: synchronize all channels
Always use the same number of samples from each channel's ring
buffer.  This ensures that all channels are kept in sync.
2010-03-10 19:48:34 +01:00
Max Kellermann
5842015b90 decoder/ffmpeg: fix indentation 2010-03-07 19:20:34 +01:00
Max Kellermann
ae56901863 decoder/ffmpeg: implement the libavutil log callback
Pass everything to the GLib logging library.  No direct stderr access.
2010-02-27 19:32:59 +01:00
Max Kellermann
4e364854ab decoder/ffmpeg: don't close the AVFormatContext after open failure
The pointer is invalid if av_open_input_file() fails.
2010-02-27 19:25:56 +01:00
Max Kellermann
a3645984cd command: "update" checks if the path is malformed
This is a very basic check, which only ensures that the path does not
begin with a slash, doesn't have double slashes and the special names
"." and ".." are forbidden.
2010-02-27 19:01:17 +01:00
Max Kellermann
43cf4e97b9 uri: allow leading dots, but explicitly exclude "." and ".."
Dots at the beginning of an URI segment are ok, as long as the special
names "." and ".." are not used.
2010-02-27 18:56:47 +01:00
Max Kellermann
795578ef95 update: log start/finish of the update thread 2010-02-27 16:41:54 +01:00
Max Kellermann
96493e0333 replay_gain: optionally use hardware mixer to apply replay gain
Add an option for each audio output which enables the use of the
hardware mixer, instead of the software volume code.

This is hardware specific, and assumes linear volume control.  This is
not the case for hardware mixers which were tested, making this patch
somewhat useless, but we will use it to experiment with the settings,
to find a good solution.
2010-02-17 08:14:07 +01:00
Max Kellermann
48b49e2303 replay_gain: fall back to track gain if album gain is unavailable 2010-02-17 08:13:34 +01:00
Max Kellermann
752dfb3d95 replay_gain: reimplement as a filter plugin
Apply the replay gain in the output thread.  This means a new setting
will be active instantly, without going through the whole music pipe.
And we might have different replay gain settings for each audio output
device.
2010-02-17 07:23:13 +01:00
Max Kellermann
5e0117b444 replay_gain_info: allocate the struct statically
Don't allocate each replay_gain_info object on the heap.  Those
objects who held a pointer now store a full replay_gain_info object.
This reduces the number of allocations and heap fragmentation.
2010-02-17 07:22:44 +01:00
Max Kellermann
c05e6a1275 replay_gain_info: use INFINITY to mark undefined values
The previous patch not only moved code, it also changed the check.
Negative gain values seem to be valid after all, there just was the
"magic" value 0.0 which means "not available".  This patch changes the
"magic" value to "INFINITY", and uses the C99 function isinf() to
check.  It might have been a better idea to use "NAN", but the "NAN"
macro is a GNU extension.
2010-02-17 07:07:00 +01:00
Max Kellermann
b21e4d9a58 replay_gain_state: moved code to replay_gain_tuple_scale()
Moved (and renamed) the function calc_replay_gain_scale() to
replay_gain_info.c.
2010-02-15 21:23:24 +01:00
Max Kellermann
eeef501ed8 replay_gain: added function defined()
This function determines whether replay gain data is available.
2010-02-15 19:09:24 +01:00
Max Kellermann
f4e9275f7c filter/volume: assign dest_size_r early, eliminate one 2010-02-15 19:09:09 +01:00
Max Kellermann
f672657388 Makefile.am: remove sticker.xml 2010-02-08 11:55:03 +01:00
Max Kellermann
32441175f4 doc: deleted sticker specification proposal
This document has been unmaintained for more than a year now (since
the day it was submitted).
2010-02-08 11:39:16 +01:00
Max Kellermann
059d1dc7f2 command: "listplaylist" dumps playlist files
Same for "listplaylistinfo".
2010-02-08 11:11:43 +01:00
Max Kellermann
7fbb856eee playlist_queue: moved code to playlist_mapper.c 2010-02-08 10:56:28 +01:00
Max Kellermann
bb2aa70ec6 playlist_queue: moved check_translate_song() to playlist_song.c 2010-02-08 10:28:12 +01:00
Max Kellermann
8e34c59c82 song_print: song_print() returns void
The only "return" statement always returns 0.
2010-02-08 10:21:15 +01:00
Anton Khirnov
766b9fd453 ffmpeg: read more metadata. 2010-02-02 17:55:58 +01:00
Tim Phipps
891dab7b91 decoder/flac: fix replay gain (short-circuiting "or") 2010-01-28 20:54:04 +01:00
Max Kellermann
7a24e496d5 configure.ac: new libwrap autoconf test
Use MPD_AUTO_RESULT().  Don't force libwrap by default.
2010-01-20 21:42:43 +01:00
Davide Camurri
7a62818ffd client: optionally use libwrap 2010-01-20 21:04:07 +01:00
Max Kellermann
acb0ff1ea8 decoder/wavpack: fixed WVC URI
Pass the current URI to wavpack_open_wvc().
2010-01-20 12:08:44 +01:00
Max Kellermann
2acad9fe1e decoder/ffmpeg: call decoder_timestamp() once per packet 2010-01-18 13:11:04 +01:00
Max Kellermann
8914ebc964 decoder/ffmpeg: merged ffmpeg_helper() into ffmpeg_decode() 2010-01-18 11:06:09 +01:00
Max Kellermann
5477c31160 decoder/ffmpeg: optimized the stream_tag() method
Don't use the function ffmpeg_helper(), don't initialize the codec.
2010-01-18 11:06:04 +01:00
Max Kellermann
acd3f8cd91 decoder/ffmpeg: free AVFormatContext on error
Fix a memory leak in some code paths.
2010-01-18 11:05:15 +01:00
Max Kellermann
ca1fc13116 decoder_api: removed function decoder_get_uri()
Use input_stream.uri.
2010-01-18 10:21:57 +01:00
Max Kellermann
9cb7760c5e input_stream: added attribute "uri" 2010-01-18 10:18:41 +01:00
Max Kellermann
fb9bd53328 input_stream: added function input_stream_deinit()
All close() implementations must call this method.
2010-01-18 09:57:53 +01:00
Max Kellermann
a0a26d3341 Merge release 0.15.8 from branch 'v0.15.x
Conflicts:
	Makefile.am
	NEWS
	configure.ac
	src/decoder/ffmpeg_decoder_plugin.c
	src/decoder_thread.c
2010-01-18 07:52:58 +01:00
Max Kellermann
9d4b7ab113 decoder_thread: don't fall back to "mad" unless no plugin matches
When all plugins have failed, MPD used to fall back to the "mad"
decoder plugin, to handle those radio streams without a Content-Type
response header.  This however leads to unexpected results (garbage
being played) when the stream isn't really mp3.  Since we care little
about "bad" streams, we shouldn't have hacks which have bad side
effects.

Let's get rid of this hack now!  Only try to "mad" plugin if there was
no match at all (Content-Type, path suffix) and no other plugin has
been tried.
2010-01-17 17:09:10 +01:00
Max Kellermann
8ac776c58b decoder_thread: don't try a plugin twice (MIME type & suffix)
Manage a linked list of plugins which were already tried.
2010-01-17 16:47:04 +01:00
Max Kellermann
006f4be71c fd_util: include ws2tcpip.h for socklen_t 2010-01-17 14:48:52 +01:00
Max Kellermann
163e05f5a0 fd_util: fixed WIN32 versions of pipe()
Use the correct parameter name.
2010-01-17 14:43:15 +01:00
Max Kellermann
02526eda86 text_file: don't strip trailing whitespace
Only delete the newline characters (\n and optionally \r).  This
allows the database file to store file names with trailing whitespace.
2010-01-17 12:52:11 +01:00
Max Kellermann
828a5f552f output/pulse: clear the "mainloop" attribute on error
When enabling the pulse device fails, clear po->mainloop after
pa_threaded_mainloop_free() has finished.  This is important for the
assertions.

Two wrong g_free() calls were also removed.
2010-01-17 12:03:22 +01:00
Max Kellermann
26841b6058 output/alsa: support packed 24 bit samples 2010-01-17 00:43:24 +01:00
Max Kellermann
1abfcc56af audio_format: support packed 24 bit samples 2010-01-16 23:44:54 +01:00
Max Kellermann
da47afe7d1 output/alsa: probe all sample formats in a loop
More code simplification.  Probe all formats, no matter which input
format.
2010-01-16 23:44:52 +01:00
Max Kellermann
96546c1a8a output/alsa: merged code into alsa_output_try_format()
Remove the debug log messages, because they are duplicate (see
ao_open() in output_thread.c).
2010-01-16 23:44:50 +01:00
Max Kellermann
579a8a96ea output/alsa: pass sample_format to get_bitformat() 2010-01-16 23:44:48 +01:00
Max Kellermann
79848e3414 output/alsa: moved code to alsa_output_setup_format() 2010-01-16 23:44:42 +01:00
Max Kellermann
87c861cae3 test/run_convert: implement a GLib log callback
Log to stderr instead of the default stdout.  We need a pristine
stdout for the conversion result.
2010-01-16 23:44:40 +01:00
Max Kellermann
8f326a33ee test/run_convert: use fifo_buffer to adapt to odd sample sizes
Ensure that the pcm_convert() length argument is aligned to the sample
size.
2010-01-16 23:25:58 +01:00
Max Kellermann
a942384fbf decoder/flac: support streams without STREAMINFO block 2010-01-06 10:17:16 +01:00
Max Kellermann
de0cdee4aa decoder/flac: pass bits_per_sample to flac_sample_format()
Easier to reuse the function.
2010-01-06 09:55:20 +01:00
Max Kellermann
550c9319e9 decoder/flac: moved decoder initialization to _flac_common.c
Invoke decoder_initialized() in the libFLAC metadata callback.  This
merges code from the FLAC and the OggFLAC decoder plugin into the
common library.
2010-01-06 09:50:34 +01:00
Max Kellermann
ae9c02b3a8 decoder/flac: remember audio_format, not stream_info 2010-01-06 09:00:32 +01:00
Max Kellermann
6f6d47dd20 decoder/flac: removed CUE sheet support
This feature has been moved to the "flac" playlist plugin.
2010-01-06 08:52:35 +01:00
Max Kellermann
030e61115c playlist: added a FLAC playlist plugin
This playlist plugin handles FLAC files with embedded CUE sheets.
2010-01-06 08:50:52 +01:00
Max Kellermann
d6d5caae23 decoder/flac: moved flac_tag_load() to flac_metadata.c
Make this code is reusable.
2010-01-06 08:50:35 +01:00
Max Kellermann
382691179f tag: fixed memmove() size argument in tag_delete_item()
This function has always been broken, but fortunately nobody used it.
2010-01-06 08:48:23 +01:00
Max Kellermann
01c5cb985c playlist_queue: convert absolute paths
Accept absolute paths if they point into the music directory.
2010-01-06 08:00:35 +01:00
Max Kellermann
8a5d2c3c83 playlist_queue: use the "uri" variable earlier
Preparation for the next patch.
2010-01-06 08:00:02 +01:00
Max Kellermann
2f8135ef8b input/rewind: enable the "rewind" wrapper for all non-seekable streams
Don't limit the "rewind" input plugin to CURL streams.
2010-01-04 21:45:32 +01:00
Max Kellermann
9b9abff972 renamed decoder plugin sources
Make it X_decoder_plugin.c.
2010-01-04 21:45:32 +01:00
Max Kellermann
c69cc31de0 decoder/mad: fix build error without libid3tag 2010-01-04 21:45:32 +01:00
Max Kellermann
241e94936f replay_gain: moved code to replay_gain_config.c 2010-01-04 21:02:47 +01:00
Max Kellermann
1330274ffc replay_gain: moved code to replay_gain_info.c 2010-01-04 21:02:36 +01:00
Max Kellermann
0e183d3fa1 replay_gain: refactor API, move code to replay_gain_state.c
The replay_gain_state struct holds the precalculated scale factor,
which is removed from struct replay_gain_info.
2010-01-04 20:54:01 +01:00
Max Kellermann
cd8f92c928 decoder_api: added function decoder_replay_gain()
This function replaces the replay_gain_info parameter for
decoder_data().  This allows the decoder to announce replay gain
changes, instead of having to pass the same object over and over.
2010-01-04 20:42:49 +01:00
Max Kellermann
e58b4f773f replay_gain: added function replay_gain_info_dup() 2010-01-04 20:42:49 +01:00
Max Kellermann
604ca50b65 valgrind.suppressions: added new suppressions 2010-01-04 20:42:49 +01:00
Max Kellermann
537e353546 replay_gain: removed overly verbose debug message
Don't log "ReplayGain is missing".
2010-01-02 23:46:44 +01:00
Max Kellermann
f95d7b13da configure.ac: disable -Wdeclaration-after-statement
Allow declaration after statement.
2010-01-02 23:46:42 +01:00
Max Kellermann
915182bcb8 output_all: reset elapsed_time at song border
Another quirk fixed: after the last chunk of a song has been played,
the "elapsed_time" variable is set to the chunk's time stamp.  When
the client receives the PLAYER idle event and asks MPD for the current
time stamp, MPD will return the last time stamp of the previous song
when it hasn't played the first chunk of the current song yet.
2010-01-02 21:16:51 +01:00
Max Kellermann
d3b763a48c input_stream: return allocated input_stream objects
Major API redesign: don't let the caller allocate the input_stream
object.  Let each input plugin allocate its own (derived/extended)
input_stream pointer.  The "data" attribute can now be removed, and
all input plugins simply cast the input_stream pointer to their own
structure (with an "struct input_stream base" as the first attribute).
2010-01-01 17:25:07 +01:00
Max Kellermann
816b6ad4a7 updated valgrind.suppressions
Mostly OpenSSL and GLib one-time allocations.
2010-01-01 17:24:47 +01:00
Max Kellermann
3adfbfe36d configure.ac: expose variables OPENAL_CFLAGS and OPENAL_LIBS
Don't add these to the global MPD_CFLAGS and MPD_LIBS.  This allows
test programs to link without libopenal.
2010-01-01 17:10:10 +01:00
Avuton Olrich
9d3865cb95 Update copyright notices. 2009-12-31 20:58:43 -08:00
Max Kellermann
05cde5810a decoder: switch a bunch of plugins to stream_tag()
This patch changes the following decoder plugins to implement
stream_tag() instead of tag_dup():

 faad, ffmpeg, mad, modplug, mp4ff, mpcdec, oggflac

This simplifies their code, because they do not need to take care of
opening/closing the stream.
2009-12-31 18:32:09 +01:00
Max Kellermann
6b96f5d566 decoder_plugin: added method stream_tag()
This is like tag_dup(), but works with an input_stream object instead
of a file path.
2009-12-31 18:27:48 +01:00
Max Kellermann
7a2e07e124 song_update: use decoder_plugin_tag_dup()
Minor code simplification.
2009-12-31 18:27:40 +01:00
Max Kellermann
aad05fd138 archive: use reference counting for archive+input
Make the input_stream implementation hold a reference on the
archive_file object.  Allow the caller to "close" the archive_file
object immediately, no matter if the open_stream() method has
succeeded or not.
2009-12-31 18:27:35 +01:00
Max Kellermann
032c5376ad refcount: library for reference counting 2009-12-31 16:26:39 +01:00
Max Kellermann
0cc3b98bd9 input/archive: don't initialize input_stream.ready
The archive plugin should decide this.
2009-12-31 16:26:34 +01:00
Max Kellermann
c157711eaf archive/bz2: allocate buffer statically
Reduce the number of malloc()/free() calls.
2009-12-31 16:26:14 +01:00
Max Kellermann
2632794578 archive/bz2: added struct bz2_input_stream
Don't use the bz2_archive_file object for the input_stream.
2009-12-31 16:25:21 +01:00
Max Kellermann
3ddf7b620c archive/iso9660: added struct iso9660_input_stream
Don't use the iso9660_archive_file object for the input_stream.
2009-12-31 16:13:09 +01:00
Max Kellermann
131cb0598a archive/iso9660: set input_stream.{size,offset} 2009-12-31 16:13:09 +01:00
Max Kellermann
9862521aec archive/zzip: added struct zzip_input_stream
Don't use the zzip_archive object for the input_stream.
2009-12-31 16:00:12 +01:00
Max Kellermann
55fbb67cfb archive/zzip: set input_stream.{size,offset} 2009-12-31 15:59:43 +01:00
Max Kellermann
a0384aaead Makefile.am: added unit tests for the archive plugins 2009-12-31 15:50:59 +01:00
Max Kellermann
efc885a9dc Merge vorbis+icy fixes from branch 'v0.15.x'
Conflicts:
	Makefile.am
	NEWS
	configure.ac
	src/input/curl_input_plugin.c
	src/input_stream.c
2009-12-30 23:24:11 +01:00
Max Kellermann
333e11d0eb Merged release 0.15.7 from branch 'v0.15.x'
Conflicts:
	NEWS
	configure.ac
	src/decoder_api.c
2009-12-27 20:10:45 +01:00
Max Kellermann
58da24b1cb playlist_list: wait for the input stream to become ready
Without this, seeking may cause MPD to crash.
2009-12-27 16:53:47 +01:00
Max Kellermann
bb4cef6a93 playlist/cue: fill song.start_ms, .end_ms
This patch adds practical usefulness to the CUE playlist plugin.
2009-12-27 16:08:53 +01:00
Max Kellermann
5787f73704 decoder, player: support song ranges
Seek the decoder to the start of the range before beginning with
playback.  Stop the decoder when the end of the range has been
reached.  Add the start position to the seek position.  Expose the
duration of the range, not the full song file.
2009-12-27 16:08:53 +01:00
Max Kellermann
201316cd67 playlist_queue: resolve relative URIs, database lookup
Prepend the playlist's base URI to relative song URIs.  Look up songs
in the database (if the URI refers to a local song file).  Merge
existing database metadata with metadata from the playlist plugin.
2009-12-27 16:08:53 +01:00
Max Kellermann
a038bca745 song: added support for selecting a time range
Added attributes start_ms, end_ms.  This allows us to address a
portion of a song file (important for CUE support).  There is no
support yet for storing these attributes in the state file.
2009-12-27 14:46:04 +01:00
Max Kellermann
cf38505d8f playlist_queue: load playlists from music directory
Try the playlist directory first, and if that file does not exist, try
the same relative path within the music directory.
2009-12-27 14:17:26 +01:00
Max Kellermann
af964e8929 uri: added function uri_safe_local() 2009-12-27 14:17:25 +01:00
Max Kellermann
554b2b0ed9 playlist/cue: return the original song URI
The caller should be responsible for building the absolute URI.
2009-12-27 14:17:21 +01:00
Max Kellermann
627975e897 playlist_queue: pass const string to playlist_open_path_into_queue() 2009-12-27 14:17:13 +01:00
Max Kellermann
6622d69fda song: added function song_get_duration() 2009-12-26 13:56:35 +01:00
Max Kellermann
216dff98d2 playlist_queue: try open by URI first
If that fails, try opening the file as a stream.
2009-12-26 03:01:52 +01:00
Max Kellermann
032354e65c playlist_list: use uri_get_suffix() 2009-12-26 03:01:50 +01:00
Max Kellermann
115d26608b uri: check presence of slash in suffix
If there's a slash in the uri_get_suffix() return value, then it's
malformed.  Return NULL in this case.
2009-12-26 03:01:48 +01:00
Max Kellermann
bad350bc18 decoder_api: added function decoder_timestamp()
Remove the data_time parameter from decoder_data().  This patch
eliminates the timestamp counting in most decoder plugins, because the
MPD core will do it automatically by default.
2009-12-26 03:01:43 +01:00
Max Kellermann
870436a592 output_init: use the normalize filter plugin
Use the plugin instead of the glue code in normalize.c.  This is used
wrapped inside a "autoconv" filter, to enable normalization for all
input file formats.
2009-12-25 17:51:08 +01:00
Max Kellermann
6a17233f78 filter: added the "autoconvert" filter plugin 2009-12-25 17:51:05 +01:00
Max Kellermann
b54bde6f2b filter_plugin: allow open() to force an input format
Make the audio_format argument non-const.  Allow the open() method to
modify it, to indicate that it wants a different input audio format
than the one specified.  Check that condition in chain_filter_open(),
and fail.
2009-12-25 17:29:41 +01:00
Max Kellermann
d2051c7f50 filter/volume: support 32 bit samples
The pcm_volume library supports 32 bit samples, there's no reason to
disallow it in the filter plugin.
2009-12-25 17:29:27 +01:00
Max Kellermann
bd29f7e3c8 filter/route: check configured channel count in method init()
Detect misconfiguration during MPD startup, not when playback begins.
2009-12-25 16:39:47 +01:00
Max Kellermann
c3bb81abec filter/route: route_filter_parse() returns bool
Indicate success and error.
2009-12-25 16:39:45 +01:00
Max Kellermann
d469c8137f filter_registry: removed the "chain" plugin from filter_plugins
This plugin cannot be configured.
2009-12-25 16:39:41 +01:00
Max Kellermann
c374a7d3f4 filter/chain: return NULL if a filter() method has failed
Don't close child filters in the filter() method.
2009-12-25 16:28:17 +01:00
Max Kellermann
3679d5bd7a playlist: added CUE playlist plugin
This plugin is the groundwork for MPD's future generic CUE sheet
support.  That's not complete yet, e.g. there is no way for a playlist
plugin to address an arbitrary position within a music file.
2009-12-16 22:19:01 +01:00
Max Kellermann
c128f2dd7e playlist_list: support URI suffix match
When no plugin matches the URI scheme, try the file name suffix.
2009-12-16 22:17:16 +01:00
Max Kellermann
843717d25c playlist_list: check if open method is present
Use open_uri() / open_stream() only after checking that they are
implemented.
2009-12-16 22:16:47 +01:00
Max Kellermann
50ea6a4b5c cue_tag: added song duration support
Get duration from track_get_length().
2009-12-16 22:15:27 +01:00
Max Kellermann
5649f22322 cue_tag: check cd_get_track()!=NULL 2009-12-16 20:55:19 +01:00
Max Kellermann
b89281411f cue_tag: added function cue_tag()
Merge code from cue_tag_file() and cue_tag_string().
2009-12-16 20:49:03 +01:00
Max Kellermann
67c41033c1 cue_tag: merged code into cue_tag_merge() 2009-12-16 17:27:52 +01:00
Max Kellermann
59534b92d2 cue_tag: pass "const" string to cue_tag_string() 2009-12-16 17:26:20 +01:00
Max Kellermann
41a48b14e3 cue_tag: changed runtime checks to assertions
It's illegal to pass NULL here.  This should not be ignored silently.
2009-12-16 17:26:20 +01:00
Max Kellermann
5821bd1a21 cue_tag: fixed indent and code style 2009-12-16 17:18:56 +01:00
Max Kellermann
c5cdac9609 cue_tag: tag_new() cannot fail
Removed the NULL check.
2009-12-16 17:18:23 +01:00
Max Kellermann
b7f55ad392 cue_tag: include cleanup 2009-12-16 17:13:26 +01:00
Max Kellermann
71fee09744 archive_plugin: use GError in the open() method 2009-12-16 17:10:19 +01:00
Max Kellermann
f9af1a445e input/archive: check for archive_file_open() errors
This fixes a NULL pointer dereference in case of archive plugin
failure.
2009-12-16 17:10:15 +01:00
Max Kellermann
0bc8c0c1da archive_plugin: wrap method calls
Make archive_file a "real" struct, extended by all plugins.  Add the
plugin pointer to it.  Wrap all method calls in functions.
2009-12-16 17:09:58 +01:00
Max Kellermann
74156d5bed archive_plugin: pass const string to method open() 2009-12-16 17:09:29 +01:00
Max Kellermann
b04adde7ab archive: added a C header for each plugin
Moved the archive plugin "extern" declarations into each plugin
header.
2009-12-16 15:57:16 +01:00
Max Kellermann
3f64ac04b8 archive/bz2: removed the bz2_context typedef
Use the raw struct name instead.
2009-12-16 15:56:42 +01:00
Max Kellermann
b009970af7 archive/bz2: renamed archive sources and plugin variables 2009-12-16 15:55:48 +01:00
Max Kellermann
bd97586cc4 archive/iso: renamed plugin to "iso9660"
Based on libiso9660.
2009-12-16 15:55:37 +01:00
Max Kellermann
c3e0fbd9e4 archive/bz2: use g_path_get_basename()
Use g_path_get_basename() instead of manually filtering the path
name.  Big advantage: g_path_get_basename() cannot fail.
2009-12-16 15:53:57 +01:00
Alam Arias
b05ba0286e updated mp4ff decoder about input_stream_seek 2009-12-16 06:47:15 +01:00
Max Kellermann
228b03edf8 input_stream: return errors with GError 2009-12-15 23:12:11 +01:00
Max Kellermann
d000d31355 encoder/flac: fix write callback prototype for libFLAC 1.1.2 2009-12-15 23:12:06 +01:00
Max Kellermann
971c9671f6 Makefile.am: link test/run_decoder with timer.c
For the fluidsynth plugin.
2009-12-15 23:11:57 +01:00
Max Kellermann
3d95226f2b decoder_internal: decoder_input_buffer() returns bool
This fixes a regression: a boolean value was returned from
decoder_input_buffer(), but the caller chose to do a "<= 0"
comparison.
2009-12-15 22:24:00 +01:00
Max Kellermann
b12072e6d9 input/archive: use g_path_is_absolute()
.. instead of manually checking pathname[0]=='/'.
g_path_is_absolute() is portable.
2009-12-15 21:26:16 +01:00
Max Kellermann
03427d4eff archive/bz2: simplified error handling, short read
Don't attempt to fill the whole buffer in the read() method, return
whatever libbz2 provides with the first successful BZ2_bzDecompress().
2009-12-15 21:09:13 +01:00
Max Kellermann
a627a703ca archive/bz2: no CamelCase 2009-12-15 20:53:29 +01:00
Max Kellermann
440cfc8052 archive/zzip: removed the "zip_context" typedef
Use the raw struct name.
2009-12-15 20:49:38 +01:00
Max Kellermann
c959148ed1 archive/zip: renamed plugin to "zzip"
This plugin is based on libzzip.
2009-12-15 20:29:44 +01:00
Max Kellermann
530e480748 Merge branch 'v0.15.x'
Conflicts:
	src/archive/bz2_plugin.c
	src/archive_api.h
	src/input/file_input_plugin.c
	test/run_input.c
2009-12-15 20:26:38 +01:00
Max Kellermann
95c3f283ea input/file: don't fall back to parent directory
This code has never made any sense, and has broken some of the archive
plugin.
2009-12-15 19:03:11 +01:00
Max Kellermann
aef6609f4f archive_api.h: moved struct archive_plugin to archive_plugin.h 2009-12-15 19:02:24 +01:00
Max Kellermann
6b728e4756 archive_list: don't include archive_api.h
Use struct forward declarations.
2009-12-15 19:02:17 +01:00
Max Kellermann
f1ecd9eac8 archive_list: iterate with NULL check
Don't use num_archive_plugins.
2009-12-15 15:31:00 +01:00
Max Kellermann
243c96304b archive/bz2: bz2_fillbuffer() returns bool 2009-12-15 09:08:30 +01:00
Max Kellermann
e3597e648c archive/bz2: fixed indentation 2009-12-15 09:00:34 +01:00
Max Kellermann
357037f7ab archive/bz2: g_malloc() cannot fail 2009-12-15 09:00:18 +01:00
Max Kellermann
9715218d40 Makefile.am: distribute AudioCompress/config.h
This fixes "make distcheck".
2009-12-15 07:38:30 +01:00
Max Kellermann
c0b8c2c73b Makefile.am: install glib_compat.h 2009-12-14 23:26:23 +01:00
Max Kellermann
51d0687377 fixed several gcc warnings on unused debug variables 2009-12-14 23:22:51 +01:00
Max Kellermann
849d7895dc decoder/mikmod: fixed gcc uninitialized warning
Removed local variable "sample_rate".
2009-12-14 23:20:14 +01:00
Max Kellermann
850e213261 scripts/test.sh: added sndfile/modplug decoder test 2009-12-14 23:19:13 +01:00
Max Kellermann
786c1f035f input_plugin: method init() returns errors with GError
Not used by any plugin currently, but this eliminates the g_error()
call in input_plugin_config(), so it's worth it.
2009-12-14 23:16:18 +01:00
Max Kellermann
f70d2f58a1 input_stream: moved input_stream_global_init() to input_init.c 2009-12-14 22:53:13 +01:00
Max Kellermann
67b0ab717e input_stream: moved plugin list to input_registry.c 2009-12-14 22:49:46 +01:00
Max Kellermann
f7420dbfe1 input_stream: make input_plugins NULL terminated
This is easier to traverse.
2009-12-14 22:43:00 +01:00
Max Kellermann
400600ffff filter: added normalize filter plugin
Wrap the AudioCompress library in a filter plugin.
2009-12-14 22:24:00 +01:00
Albin Eldstål-Damlin
940e66bb89 Fix and use filter_configured_new() 2009-12-14 22:23:05 +01:00
Albin Eldstål-Damlin
69391dadda Proper error reporting from filter_config 2009-12-14 22:18:28 +01:00
Albin Eldstål-Damlin
ff3393ebf1 Fixed memory leak on incorrect route configuration 2009-12-14 22:18:28 +01:00
Albin Eldstål-Damlin
0ac0bd26e7 Split filter_config into its own module 2009-12-14 22:12:58 +01:00
Max Kellermann
bf6258f582 test/run_{decoder,filter}: implemented GLib log callback
Log to stderr, not to stdout (which broke PCM output).
2009-12-14 21:26:57 +01:00
Albin Eldstål-Damlin
8587fcbb93 Error reporting, pcm_buffer, performance tweaks 2009-12-14 20:41:09 +01:00
Albin Eldstål-Damlin
a4fbf772c1 Initial filter chain and filter configuration for outputs. 2009-12-14 17:56:31 +01:00
Albin Eldstål-Damlin
e28c5a0beb Initial (statically configured) route filter plugin 2009-12-14 17:41:28 +01:00
Albin Eldstål-Damlin
c38b9490a8 Minor documentation fix 2009-12-14 17:41:10 +01:00
Max Kellermann
c3085d7b61 Merge branch 'v0.15.x'
Conflicts:
	src/decoder/ffmpeg_plugin.c
2009-12-14 17:40:50 +01:00
Thomas Jansen
179502fe93 decoder_api: prefer stream_tag over decoder_tag
If both tags (stream and decoder) are present, we prefer the stream tag.
Fixes #2698, where ICY tag contained useful information, but was
overwritten with bogus decoder tag data.
2009-12-07 14:52:48 +01:00
Viliam Mateicka
b6b377edd1 pcm_volume: change old code to use format instead of bits 2009-12-03 20:56:57 +01:00
Viliam Mateicka
a76097210f encoders: remove unnessesary pointers to const strings 2009-12-03 20:43:13 +01:00
Viliam Mateicka
ac0bf1a445 httpd: use get_mime_type to determine encoder content 2009-12-03 20:27:08 +01:00
Viliam Mateicka
7b80e73810 encoders: implement new get_mime_types method 2009-12-03 20:11:32 +01:00
Viliam Mateicka
6d11711a01 encoder: add get_mime_type() method to determine content type by httpd output plugin 2009-12-03 19:39:34 +01:00
Viliam Mateicka
bae03e173e pcm_mix: change old code to use format instead of bits 2009-12-03 17:03:21 +01:00
Viliam Mateicka
07b388f8d4 null_encoder: use pcm_buffer 2009-12-03 17:03:20 +01:00
Viliam Mateicka
4809213676 flac_encoder: add support for libFLAC < 1.1.3 2009-12-03 14:53:30 +01:00
Jeffrey Middleton
c77fa296bc compress: add config.h
This includes some default values of #defined constants used in the
code; it won't compile without it.
2009-12-02 21:14:35 -06:00
Max Kellermann
c412d6251e audio_format: changed "bits" to "enum sample_format"
This patch prepares support for floating point samples (and probably
other formats).  It changes the meaning of the "bits" attribute from a
bit count to a symbolic value.
2009-12-02 22:29:50 +01:00
Max Kellermann
68c2cfbb40 test: added normalize test program 2009-12-02 21:56:02 +01:00
J. Shagam
4076523198 compress: upgraded to AudioCompress 2.0
Copied sources from
http://beesbuzz.biz/code/audiocompress/AudioCompress-2.0.tar.gz

[mk: created this patch under fluffy's name and fixed some gcc
signed/unsigned comparison warnings]
2009-12-02 18:11:53 +01:00
Max Kellermann
3857bb9990 decoder/mpcdec: set 24 bit sample format
This fixes a regression due to a typo caused by "decoder: use
audio_format_init_checked()".
2009-11-25 08:59:13 +01:00
Max Kellermann
300f936228 pcm_mix: implemented 32 bit support 2009-11-19 21:00:54 +01:00
Max Kellermann
1358428031 pcm_volume: implemented 32 bit support
Support 32 bit samples with software mixer.
2009-11-19 21:00:50 +01:00
Max Kellermann
5a480137d2 test: added program to test pcm_convert.c 2009-11-19 21:00:46 +01:00
Max Kellermann
943bafbbc8 test/software_volume: check for errors 2009-11-19 21:00:39 +01:00
Max Kellermann
b772f26213 test/software_volume: fixed audio_format parser
Assign default value only if none was given on the command line.
2009-11-19 20:36:04 +01:00
Max Kellermann
c33bbd947b Merged release 0.15.6 from branch 'v0.15.x'
Conflicts:

	NEWS
	configure.ac
2009-11-19 19:59:34 +01:00
Viliam Mateicka
d37b4bb199 cmdline: print out list of encoders in --version info 2009-11-17 20:39:26 +01:00
Viliam Mateicka
ea92dee1ae encoder: let wave encoder to use pcm_buffer, pcm conversion code cleanup 2009-11-17 20:39:21 +01:00
Viliam Mateicka
5420f9ae76 encoder: introducing flac encoder plugin 2009-11-17 19:41:35 +01:00
Max Kellermann
39404725f0 output/openal: use audio_format_to_string() 2009-11-15 16:20:20 +01:00
Max Kellermann
5d1e5f4ea0 crossfade: use audio_format_valid() in assertion 2009-11-15 15:39:29 +01:00
Max Kellermann
5184476682 valgrind.suppressions: added entry for g_main_context_default() 2009-11-14 23:51:49 +01:00
Max Kellermann
141cbc60b9 decoder/audio: eliminate the "bits" variable
Pass the audiofile_setup_sample_format() result to
audio_format_init_checked().
2009-11-14 23:35:37 +01:00
Max Kellermann
2c1fb48318 decoder/audiofile: moved code to audiofile_setup_sample_format() 2009-11-14 23:22:14 +01:00
Max Kellermann
1dfadf4815 decoder/modplug: count frame position
Don't maintain the current time stamp in a floating point variable,
because this is subject to rounding errors.
2009-11-14 22:30:57 +01:00
Max Kellermann
f5b9e3c064 decoder/modplug: floating point division for song duration
More exact total time.
2009-11-14 22:27:27 +01:00
Max Kellermann
ff70dbd316 decoder/modplug: check ModPlug_Read() < 0
Negative return values are not documented here, but since the function
prototype is signed, let's be sure.
2009-11-14 22:27:04 +01:00
Max Kellermann
dd4625ce13 decoder/mikmod: count frame position
Don't maintain the current time stamp in a floating point variable,
because this is subject to rounding errors.
2009-11-14 02:24:42 +01:00
Max Kellermann
1648c7aa5b decoder/mikmod: sample rate is configurable
The new option "sample_rate" sets the sample rate for libmikmod.
2009-11-14 02:24:42 +01:00
Max Kellermann
edaf017908 decoder/mikmod: set drv_name and drv_version from PACKAGE/VERSION 2009-11-14 02:24:42 +01:00
Max Kellermann
2c7bf61e68 decoder/mikmod: no CamelCase 2009-11-14 02:07:41 +01:00
Max Kellermann
7efb548921 decoder/mikmod: removed the struct mod_Data 2009-11-14 02:07:41 +01:00
Max Kellermann
0c5329aedc decoder/mikmod: merged open()/close() into decode()
These functions are trivial, we don't need them separate.
2009-11-14 02:07:41 +01:00
Max Kellermann
2d236e281f decoder/mikmod: static mod_Data object
Don't allocate this object, put it on the stack.
2009-11-14 02:07:41 +01:00
Max Kellermann
a6fd5819f9 doc: added decoder plugin reference 2009-11-14 02:06:23 +01:00
Max Kellermann
cef5dcc0a1 audio_format: added function audio_format_to_string()
Unified function for converting an audio_format object to a string,
for log messages and for the "status" command.
2009-11-14 01:15:26 +01:00
Max Kellermann
e5b119a324 autogen.sh: allow two minor digits in automake version 2009-11-14 01:15:26 +01:00
Max Kellermann
719990b1c5 decoder: use audio_format_init_checked()
Let the audio_check library verify the audio format in all (relevant,
i.e. non-hardcoded) plugins.
2009-11-14 00:47:22 +01:00
Max Kellermann
f47bb8c1db audio_check: checker functions for audio_format attributes
These functions are a wrapper for audio_valid_X().  On error, they
return a GError object.
2009-11-14 00:47:19 +01:00
Max Kellermann
873025a495 decoder/sidplay: correctly calculate floating point time
Internally, use only the integer time.  When needed, convert it to a
floating point seconds value.
2009-11-14 00:46:30 +01:00
Max Kellermann
409a3ed808 player_thread: corrected two assertions on "queued"
At this point, the function may be called from the SEEK handler.
2009-11-14 00:45:52 +01:00
Max Kellermann
76283c25a5 player_thread: initialize chunk->times in silence generator
When waiting for the decoder to provide more data, the player thread
generates silence chunks if needed.  However, it forgot to initialize
the chunk.times attribute, which had now an undefined value.  This
patch sets it to -1.0, meaning "value is undefined".  Add a ">= 0.0"
check to audio_output_all_check().  This fixes spurious relative
seeking errors, because sometimes, the "elapsed" value falls back to
0.0.
2009-11-12 18:41:25 +01:00
Max Kellermann
b9866e43d3 player_control: hold lock while reading status 2009-11-12 18:40:36 +01:00
Max Kellermann
9947b82cad added .#* to .gitignore
Temporary editor files.
2009-11-12 18:39:40 +01:00
Max Kellermann
5b82ffc291 include config.h in all sources
After we've been hit by Large File Support problems several times in
the past week (which only occur on 32 bit platforms, which I don't
have), this is yet another attempt to fix the issue.
2009-11-12 09:17:03 +01:00
Max Kellermann
8068fd5228 decoder/vorbis: fixed gcc "signed" warning 2009-11-12 09:16:34 +01:00
Max Kellermann
b9c610ac87 directory: include config.h
*sigh* another Large File breakage.  ino_t/dev_t this time.  We need
to include config.h in directory.h to get this straight.
2009-11-11 23:36:36 +01:00
Max Kellermann
59189160e3 decoder/wavpack: allow more than 2 channels
Remove the OPEN_2CH_MAX option.  MPD's support for surround sound is
still clunky, but we're working on it.
2009-11-11 23:03:20 +01:00
Max Kellermann
ee5d3337a7 decoder/wavpack: activate 32 bit support
MPD has been supporting 32 bit samples since version 0.15.  This patch
changes one check, and removes the 32->24 conversion code.

Note that WavPack floating point samples have 32 bits, and MPD doesn't
have a special check for floating point - therefore, this WavPack
plugin still returns 24 bit integer samples as before (until we have
float support in the MPD core).
2009-11-11 21:49:00 +01:00
Max Kellermann
4c6a8e3ca5 decoder/vorbis: initialize before entering the loop
Call decoder_initialize() before entering the loop.  We don't need to
call ov_read() before ov_info().  When the stream number changes,
check if the audio format is still the same.
2009-11-11 21:34:55 +01:00
Max Kellermann
4f38cc9cae decoder/vorbis: moved error strings to vorbis_strerror() 2009-11-11 21:12:10 +01:00
Max Kellermann
dfc09a37c9 decoder/vorbis: removed the OggCallbackData typedef
Use the struct name instead.
2009-11-11 21:09:08 +01:00
Max Kellermann
8588c21689 decoder/vorbis: fix typo in comment 2009-11-11 21:09:08 +01:00
Max Kellermann
2decc65b45 decoder/vorbis: removed redundant "bits" initialization
This is done by audio_format_init().
2009-11-11 21:03:04 +01:00
Max Kellermann
0fb877740b decoder/flac: check "seekable" in libFLAC callbacks
Return FLAC__STREAM_DECODER_SEEK_STATUS_UNSUPPORTED if this input
stream does not support seeking.
2009-11-11 21:02:52 +01:00
Max Kellermann
37181c9181 decoder/flac: moved code to flac_data_get_audio_format()
Remove the audio_format attribute, add "frame_size" instead.  The
audio_format initialization and check is moved both to
flac_data_get_audio_format().
2009-11-11 20:44:21 +01:00
Max Kellermann
08b139f37c decoder/flac: use stream_info instead of audio_format
Use the sample rate stored in the stream_info struct instead of the
audio_format struct.
2009-11-11 20:36:53 +01:00
Max Kellermann
3973aeecd2 decoder/flac: use frame header instead of audio_format
When calculating the properties of the frame, use sample_rate and
other information from the frame header instead of the stored
audio_format object.
2009-11-11 20:36:46 +01:00
Max Kellermann
5b2d32b499 decoder/oggflac: moved stream_info check to oggflac_decode() 2009-11-11 20:36:21 +01:00
Max Kellermann
e0d5ee0045 decoder/flac: calculate time stamp from current frame
Don't update a float timestamp, this will make imprecisions add up
after a while.  We already have the number of the current frame, let's
just calculate the float timestamp from that for every decoder_data()
command.  For this, we need to add the attribute "first_frame", for
CUE sheet songs.
2009-11-11 20:18:39 +01:00
Max Kellermann
d35efddd65 decoder/flac: calculate bit rate in flac_common_write()
Removed the "bit_rate" attribute from the flac_data struct.  Pass the
number of bytes since the last call to flac_common_write(), and let
it calculate the bit rate.
2009-11-11 19:52:14 +01:00
Max Kellermann
7b13776f2d decoder/flac: store the whole stream info object, not duration
We don't want to work with floating point values if possible.  Get the
integer number of frames from the FLAC__StreamMetadata_StreamInfo
object, and convert it into a float duration on demand.  This patch
adds a check if the STREAMINFO packet has been received yet.
2009-11-11 19:25:15 +01:00
Max Kellermann
f937ec9a7c decoder/flac: merge code into flac_decoder_initialize()
Wrapper for FLAC__stream_decoder_process_until_end_of_metadata(),
decoder_initialized().
2009-11-11 19:08:10 +01:00
Max Kellermann
a3f5284dc6 decoder/flac: merged code into flac_decoder_new()
Convenience wrapper for FLAC__stream_decoder_new() and
FLAC__stream_decoder_set_metadata_respond().
2009-11-11 19:07:30 +01:00
Max Kellermann
4a8cc87b4d decoder/flac: free the "pathname" variable earlier
Free the pointer right after its last use, i.e. after the
FLAC__stream_decoder_init_file() call.
2009-11-11 19:05:24 +01:00
Max Kellermann
183725733a decoder/flac: emulate FLAC__stream_decoder_init_stream()
Remove the wrapper flac_init().
2009-11-11 18:08:22 +01:00
Max Kellermann
3c1bacbdbc decoder/flac: use the new API functions
Use the type and function names of the libFLAC 1.1.3 API.  Map the new
API to the old one with macros.
2009-11-11 17:59:46 +01:00
Max Kellermann
d5ed23438a decoder/flac: removed the fake flac_ogg_init() fallback
Don't even try to call it with an old libFLAC API.
2009-11-11 17:01:14 +01:00
Max Kellermann
5bbaf0c9f1 decoder/flac: moved code to flac_compat.h 2009-11-11 16:43:34 +01:00
Max Kellermann
f2f8290242 decoder/{flac,vorbis}: include config.h for LFS
Allow those plugins to open large files on 32 bit platforms.
2009-11-11 16:37:42 +01:00
Max Kellermann
c1186693b5 decoder/flac: merged code into flac_decoder_loop()
The decoder loop of flac_decode_internal(), flac_container_decode()
and flac_filedecode_internal() is merged into this one function.  This
unifies the code, and uses the frame number to identify the end of a
CUE sub song.
2009-11-11 16:28:44 +01:00
Max Kellermann
80b220a3a6 decoder/flac: keep track of current frame number
We need this for more exact end-of-subsong detection for CUE files.
2009-11-11 15:31:17 +01:00
Max Kellermann
5cc3c4f503 Merge remote branch 'origin/v0.15.x' 2009-11-11 15:14:20 +01:00
Max Kellermann
96204ea3dc fd_util: don't call fd_set_nonblock() if open() has failed
This fixes an assertion failure.
2009-11-11 14:30:38 +01:00
Max Kellermann
9d1a34e30b added missing config.h includes for extended LFS support
All sources which might work with large files must include config.h,
to get Large File Support on 32 bit platforms.
2009-11-11 14:15:34 +01:00
Max Kellermann
69d9716f8b update: added missing config.h includes
This broke sticker and archive support.
2009-11-11 14:13:24 +01:00
Max Kellermann
707b9fea17 decoder/flac: removed redundant NULL checks
After the decoder loop, "flac_dec" is always set.
2009-11-11 08:37:21 +01:00
Max Kellermann
d605329f83 decoder/flac: moved code to flac_pcm.c 2009-11-11 07:59:22 +01:00
Max Kellermann
f6e7dffada decoder/flac: moved code to flac_metadata.c 2009-11-11 07:50:40 +01:00
Max Kellermann
43549db718 decoder/flac: return replay_gain_info object from helper function
Make the function more generic by not passing "struct flac_data" to
it.
2009-11-11 07:35:16 +01:00
Max Kellermann
7c0c8ca8d7 Merge branch 'master' of git://git.musicpd.org/metyl/mpd 2009-11-11 06:45:23 +01:00
Max Kellermann
884be8e2b3 decoder/flac: merged some code into flac_tag_apply_metadata() 2009-11-11 00:05:14 +01:00
Max Kellermann
305de100a7 decoder/oggflac: initialize the "tag" variable 2009-11-10 23:56:59 +01:00
Viliam Mateicka
79035d7ed9 wave_encoder: new encoder for streaming PCM wave files.
When using wave encoder with httpd audio output mpd can input this stream via http and audiofile decoder.
This for example opens simple way to configure lossless audio streaming port(like jack or pulseaudio does but without overhead).
Another possibility can be using it for gathering raw data for visualization plugins (If sync issue will be resolved)
2009-11-10 22:29:54 +00:00
Max Kellermann
c1a999c492 decoder/flac: don't use float to calculate song duration
Simple (up-rounding) integer division is good enough.  We're casting
the result back to an integer anyway.
2009-11-10 22:03:58 +01:00
Max Kellermann
e51d9fc6a9 decoder/flac: pass VorbisComment to comments_to_tag() 2009-11-10 21:58:19 +01:00
Max Kellermann
2f69831fb8 decoder/flac: use pcm_buffer instead of fixed buffer
This is a great simplification for flac_common_write(), because we can
convert and submit all of the buffer in one turn.  No more partial
buffers with complicated formulas.
2009-11-10 21:46:10 +01:00
Max Kellermann
b6a2ffd3d7 decoder/flac: added function flac_data_deinit()
Clean up tag and replay_gain_info there.
2009-11-10 21:42:15 +01:00
Max Kellermann
6a5f4651a1 test: fixed the read_mixer program on !HAVE_PULSE 2009-11-10 21:38:20 +01:00
Max Kellermann
70106464d3 configure.ac: enable_audiofile defaults to "auto"
Since we're using MPD_AUTO_PKG, we can auto-detect this option.  Also
fix the --enable-audiofile help string.
2009-11-10 21:27:55 +01:00
Max Kellermann
b722d3d7f3 configure.ac: require GLib 2.12
Drop the required GLib version from 2.16 to 2.12, because many current
systems still don't have GLib 2.16.  This requires several new
compatibility functions in glib_compat.h.
2009-11-10 21:14:22 +01:00
Max Kellermann
de57c21a3b Merge branch 'v0.15.x'
Conflicts:
	src/input/lastfm_input_plugin.c
	src/song_save.c
2009-11-10 21:13:03 +01:00
Max Kellermann
84917721c7 moved GLib compatibility code to glib_compat.h 2009-11-10 21:07:54 +01:00
Max Kellermann
118495d372 decoder/flac: pass void pointer to flac_convert()
That function diverts into various bit formats; it doesn't need a
typed pointer.
2009-11-10 19:57:58 +01:00
Max Kellermann
a6bee71f1a decoder/flac: pass audio_format.bits to flac_convert()
Don't use audio_format_sample_size() for identifying the sample
format.
2009-11-10 19:57:28 +01:00
Max Kellermann
e0e6813a1d fd_util: removed creat_cloexec()
Add a "mode" argument to open_cloexec() instead.
2009-11-10 16:53:24 +01:00
Max Kellermann
3d2a9d3545 fd_util: added function pipe_cloexec()
Same as pipe_cloexec_nonblock(), but doesn't set non-blocking mode.
2009-11-10 16:53:20 +01:00
Max Kellermann
6975c087e0 decoder_list: fix decoder_plugin_from_mime_type()
Copy'n'paste error: call decoder_plugin_supports_mime_type() instead
of decoder_plugin_supports_suffix().
2009-11-09 22:49:05 +01:00
Max Kellermann
54033c74e4 output/alsa: fill period buffer with silence before draining
ALSA passes full period buffers to the hardware.  If an application
doesn't finish writing a period, libasound will nonetheless send the
partial buffer (with undefined trailing data).  This causes noise at
the end of playback.  This patch attempts to track the current
position within the period buffer, and generates silence at the end,
before calling snd_pcm_drain().
2009-11-09 22:22:31 +01:00
Max Kellermann
8420f1420f player_thread: drain audio outputs at the end of the playlist
When there's no queued song, and the current one has finished playing,
first make sure that the hardware outputs have really finished playing
the last chunk: call the drain() method in all audio outputs.  Without
this patch, MPD stopped playback shortly before the ALSA sound card
had finished playing.
2009-11-09 22:22:27 +01:00
Max Kellermann
3359f8785e output_thread: added command DRAIN
This command manually drains the hardware buffer.  This is useful when
the player thread want to make sure that everything has been played.
2009-11-09 22:16:26 +01:00
Max Kellermann
96b974bc45 player_control: removed the "volatile" attribute
Our use of the "volatile" keyword was wrong from the start, and now
that we have proper locking, we can safely remove all of them.
2009-11-09 20:33:45 +01:00
Max Kellermann
1a4025420c fd_util: added missing NONBLOCK fallback for socket() 2009-11-08 22:24:02 +01:00
Max Kellermann
223b0db5bd fd_util: relicense under BSD 2-clause
We'll copy this code to libmpdclient, and that's easier if its license
is BSD.
2009-11-08 22:15:22 +01:00
Max Kellermann
2f4144e1cd utils: removed function set_nonblocking()
It's not used anymore, its features have been moved to fd_util.c.
2009-11-08 22:11:37 +01:00
Max Kellermann
f66edccffd fd_util: added O_NONBLOCK functions
Changed the wrappers for pipe(), socket(), accept().  On WIN32, this
does not work for pipe().
2009-11-08 22:11:35 +01:00
Max Kellermann
b043ade456 fd_util: fixed typo in API documentation 2009-11-08 22:07:14 +01:00
Max Kellermann
217b494cc5 encoder/null: removed empty close() method
That's an optional method.
2009-11-08 21:44:01 +01:00
Max Kellermann
5ef62312af encoder/null: removed unused audio_format attribute 2009-11-08 21:43:19 +01:00
Max Kellermann
5479ed7cfb fd_util: added API documentation 2009-11-08 21:38:52 +01:00
Max Kellermann
cac63bfd21 fd_util: unexport fd_set_cloexec()
This function is used only internally.
2009-11-08 21:38:38 +01:00
Max Kellermann
1573ea1485 inotify: set close-on-exec flag
Added wrapper for inotify_init1() to fd_util.c.
2009-11-07 19:02:53 +01:00
Max Kellermann
e3af0032b2 set the close-on-exec flag on all file descriptors
Added the "fd_util" library, which attempts to use the new thread-safe
Linux system calls pipe2(), accept4() and the options O_CLOEXEC,
SOCK_CLOEXEC.  Without these, it falls back to FD_CLOEXEC, which is
not thread safe.

This is particularly important for the "pipe" output plugin (and
others, such as JACK/PulseAudio), because we were heavily leaking file
descriptors to child processes.
2009-11-07 18:55:16 +01:00
Max Kellermann
9b21152600 decoder_thread: close input file
An input_stream_close() call was missing after today's code
reorganization.
2009-11-07 17:58:52 +01:00
Max Kellermann
c440faa94d log: redirect stdout/stderr to /dev/null if syslog is used
Don't hold a file descriptor on root's tty when syslog is used for
logging.
2009-11-07 17:48:57 +01:00
Max Kellermann
375fd5ed4c output/jack: added option "server_name" 2009-11-07 17:26:21 +01:00
Max Kellermann
ec25cda68b output_all: automatically attempt to re-enable failed outputs
When an output's enable() method has failed, and playback starts,
retry to enable it.  Without this, the user may be confused, because
he sees the device is "enabled" but cannot use it, and currently there
is no error message in the log.
2009-11-07 17:22:34 +01:00
Max Kellermann
c9f726048c output/httpd: moved code to httpd_output_bind() 2009-11-07 16:52:44 +01:00
Max Kellermann
c2251dc5a2 exclude: use GPatternSpec instead of fnmatch()
GLib's version of fnmatch() is more portable.
2009-11-07 16:29:29 +01:00
Max Kellermann
a505cbc6c9 added missing source file decoder_print.c 2009-11-07 16:28:21 +01:00
Max Kellermann
c422344190 database: I/O error handling in db_save()
Check ferror() instead of the fprintf() return value.
2009-11-07 16:20:07 +01:00
Max Kellermann
1a4cfc3d90 update_walk: log new container files 2009-11-07 16:03:25 +01:00
Max Kellermann
2f1bd39be8 command: added command "decoders"
This command prints a list of decoder plugins and their suffixes /
MIME types.
2009-11-07 15:57:22 +01:00
Max Kellermann
4624dfcb30 decoder_list: moved print_all_decoders() to cmdline.c
Export the decoder_plugins array.  The function
decoder_plugin_print_all_decoders() it is UI specific and should not
live in this backend library.
2009-11-07 15:46:45 +01:00
Max Kellermann
3546d931a1 decoder_thread: check for STOP before calling the plugin
Before calling the plugin's decode method, we should ensure that we
didn't receive a STOP command during initialization.
2009-11-07 15:37:18 +01:00
Max Kellermann
4dadb965a7 decoder_thread: moved code to decoder_input_stream_open()
This function opens the stream and waits for it to become ready;
meanwhile it checks for STOP commands.  It is code moved from
decoder_run_stream().
2009-11-07 15:35:50 +01:00
Max Kellermann
f2184db1cd decoder_thread: added local variable "dc" in decoder_run_file()
Simplify the expressions.
2009-11-07 15:24:38 +01:00
Max Kellermann
41f3f12709 output/jack: free source port names on exit
Make valgrind happy.
2009-11-07 15:17:48 +01:00
Max Kellermann
5d55b45654 decoder_list: pass previous plugin pointer to lookup functions
Remove the static integer hack, that's not thread safe and sucks.
2009-11-07 15:14:16 +01:00
Max Kellermann
e3da174fca decoder_list: moved suffix/mime_type checks to decoder_plugin.c 2009-11-07 15:14:11 +01:00
Max Kellermann
bb862a8ceb decoder_list: back to NULL terminated list
A NULL terminated list is easier to iterate.
2009-11-07 15:14:09 +01:00
Max Kellermann
9ba900486e decoder_thread: open input stream on demand
Moved the global input stream opener to decoder_run_stream().
decoder_run_file() now opens the input stream each time a plugin
provides a stream decoder method.
2009-11-07 15:10:12 +01:00
Max Kellermann
587284bae6 decoder_thread: moved plugin loops to separate functions
Tame the large decoder_run_song() function.
2009-11-07 14:57:46 +01:00
Max Kellermann
4c7bfa514f uri: added function attributes
Let gcc optimize a little bit more.
2009-11-07 14:17:28 +01:00
Max Kellermann
f9218423b9 utils: renamed stringFoundInStringArray()
No CamelCase.  Use bool instead of int.  Make both arguments
mandatory.
2009-11-06 19:50:47 +01:00
Max Kellermann
ba34d48cf0 output/jack: dynamic source port list
Same as the previous patch: create up to 16 configured source ports.
The plugin tries to do its best at guessing the right combination for
the given input file, the number of source and destination ports.
2009-11-06 18:58:35 +01:00
Max Kellermann
2598dd5109 output/jack: dynamic destination port list
Support up to 16 configured destination ports, that should really be
enough for everybody.
2009-11-06 18:55:26 +01:00
Max Kellermann
fac6e9ecdb output/jack: renamed option "ports" to "destination_ports"
Be more clear which kind of port should be configured here.
2009-11-06 01:54:58 +01:00
Max Kellermann
dbbead6e72 output/jack: renamed "output ports" to "destination ports"
Use the same name as in the libjack API documentation.
2009-11-06 01:35:19 +01:00
Max Kellermann
8cb9f9b070 playlist_queue: use playlist plugins to load from playlist_directory
This patch allows the client to load a playlist file from the playlist
directory with a plugin.  This can be used with the "load" command,
but the client has to pass the file name including the suffix.  We
will probably use the music directory in the future, to support
playlist files inside the music directory.
2009-11-06 01:09:21 +01:00
Max Kellermann
61cb5df842 playlist_queue: moved code to playlist_open_remote_into_queue() 2009-11-06 01:07:42 +01:00
Max Kellermann
7e66f34154 playlist_list: added function playlist_list_open_path()
Added an interface for loading playlists from a local file.
2009-11-06 01:07:39 +01:00
Max Kellermann
f3203b5de5 playlist: added extm3u plugin
This new plugin parses extm3u files.  Files without the "#EXTM3U"
header are still parsed by the plain old "m3u" plugin.
2009-11-06 00:41:42 +01:00
Max Kellermann
65e56ff829 playlist/{m3u,pls}: removed URI checks
The caller is responsible for verifying the song URI.
2009-11-06 00:36:32 +01:00
Max Kellermann
a4970c66ef playlist_list: rewind the stream before opening the playlist
If one plugin has failed to open the playlist, it may have consumed a
part of the stream already.  This may lead to a failure in all
following plugins.  Fix: rewind the stream before each open() call.
2009-11-06 00:11:36 +01:00
Max Kellermann
945287358b output/httpd: bind port when output is enabled
Implement the methods enable() and disable().  Bind the HTTP port in
the enable() method, but reject all incoming connections until the
output is opened.
2009-11-05 23:47:29 +01:00
Max Kellermann
979cd5a768 output/jack: support mono input
When MPD plays a mono song (audio_format.channel==1), connect only one
source port to both destination ports.
2009-11-05 20:02:04 +01:00
Max Kellermann
a68da8a475 output/jack: clear ring buffers before activating
After playback has stopped, the ring buffers may still contain
samples.  These will be played when playback is started the next
time.  We should clear the buffers each time.
2009-11-05 20:01:50 +01:00
Max Kellermann
2a9685cb3a output/jack: use jack_client_open() instead of jack_client_new()
jack_client_new() is deprecated.  This requires libjack 0.100
(released nearly 5 years ago).  We havn't been testing older libjack
versions anyway.

As a side effect, there is the new option "autostart".
2009-11-05 20:01:18 +01:00
Max Kellermann
9449006ac3 doc/user: document JACK plugin options 2009-11-05 19:55:16 +01:00
Max Kellermann
88abfc0d0f output/jack: added option "client_name"
Instead of using MPD's audio output name (setting "name"), use a
separate configuration option.  Change the default to "Music Player
Daemon".
2009-11-05 19:55:00 +01:00
Max Kellermann
4ec25b5d64 update_walk: log unrecognized files
When a song's tags could not be loaded during database update, log
this as a debug message.  Same for a song being removed because its
updated tag could not be read.
2009-11-05 08:01:29 +01:00
Max Kellermann
e96dc9a14c database: rescan after metadata_to_use change
Store a list of supported tag items in the database.  When loading a
database which does not have a matching list, we must rescan in order
to get the missing information.
2009-11-04 18:48:22 +01:00
Max Kellermann
a434c35eb4 tag: added function tag_name_parse()
Convert a string into a tag_type enum.
2009-11-04 18:47:42 +01:00
Max Kellermann
7af8c88e37 song_save: explicitly parse the colon
Clear the colon.  This simplifies all attribute parsers, because they
can now use strcmp() instead of strncmp().
2009-11-04 18:43:16 +01:00
Max Kellermann
a027bdf118 decoder_thread: initialize decoder_control.quit
If left uninitialized, then the decoder thread quits spuriously.
2009-11-04 09:44:50 +01:00
Max Kellermann
c043bf0f63 Merge branch 'master' of git://git.musicpd.org/romain/mpd 2009-11-04 09:20:13 +01:00
Romain Bignon
5744634094 sticker comments in code and example configuration file
Signed-off-by: Romain Bignon <romain@peerfuse.org>
2009-11-04 00:21:58 +01:00
Max Kellermann
a22c93b659 player_thread: on return, reset next_song only if queued
If no song was queued, then player_control.next_song might contain the
value for the next QUEUE command.  We must not reset that.
2009-11-03 23:32:37 +01:00
Max Kellermann
507606bb78 player_thread: detect finished queued song
When the decoder finishes the "queued" song very quickly (before the
"current" song finishes playing), an assertion in do_play() fails
because it thinks that it should start decoding the queued song,
although that has in fact just finished.
2009-11-03 23:29:50 +01:00
Max Kellermann
f421c8a191 player_control: moved pc.next_song assertion into lock
The assertion shouldn't access player_control.next_song without
locking it.
2009-11-03 23:17:44 +01:00
Max Kellermann
d06f8baf69 player_thread: moved code to decoding_next_song()
Simplify several expressions.
2009-11-03 22:27:40 +01:00
Max Kellermann
56b8081af6 player_thread: lock player in player_check_decoder_startup()
Don't access attributes without the lock.
2009-11-03 21:23:48 +01:00
Max Kellermann
98d30ac9cf decoder_control: pass pipe and buffer to dc_start()
Don't access decoder_control attributes directly.
2009-11-03 21:18:22 +01:00
Max Kellermann
98150f503a player_thread: lock inside player_wait_for_decoder()
Lock the player_control object when modifying its attributes.
2009-11-03 21:02:54 +01:00
Max Kellermann
6c78c21fb8 player_thread: moved code to player_dc_start() 2009-11-03 21:01:56 +01:00
Max Kellermann
7fd2097a44 decoder_control: don't check command in decoder_is_starting()
Asynchronous decoder startup is gone, and we don't need to check
command==DECODE_COMMAND_START anymore.
2009-11-03 21:01:49 +01:00
Max Kellermann
89893faa19 decoder_control: merge next_song and current_song
These two variables are redundant, we need only one of them.
2009-11-03 20:02:19 +01:00
Max Kellermann
bfa7da943c player_thread: don't set errored_song on audio error
It's not used if pc.error==PLAYER_ERROR_AUDIO.
2009-11-03 20:01:19 +01:00
Max Kellermann
734676fcfb decoder_thread: unlock the decoder while checking the stream
This is only a slight change to the previous locking behaviour: keep
the decoder unlocked during the loop, and lock it only while checking
decoder_control.command.
2009-11-03 19:21:55 +01:00
Max Kellermann
acb265d082 player_thread: don't start the decoder asynchronously
The START command returns without blocking; we don't need the
asynchronous decoder start anymore.
2009-11-03 19:18:17 +01:00
Max Kellermann
77a647fc26 decoder_thread: open input stream after command finished
Return the result to the caller more quickly.  This unifies error
handling: no error can be reported before the command is finished.
2009-11-03 19:17:52 +01:00
Max Kellermann
59ffb5b7c1 decoder_control: make the song objects const
They are just informational.
2009-11-03 19:16:25 +01:00
Jeffrey Middleton
4dc25d3908 alsa_plugin.c: workaround snd_pcm_drain bug
Reintroduce a fix from commit 52a0653 (Warren Dukes): "don't call
snd_pcm_drain unless we're already in the RUNNING state". This prevents
ALSA with dmix from sometimes hanging when snd_pcm_drain is called, e.g.
when moving from one song to the next (as in mantis issue 2634).
2009-11-02 23:58:15 -06:00
Max Kellermann
0f9f82f227 output_thread: moved code to ao_next_chunk() 2009-11-02 20:20:14 +01:00
Max Kellermann
55e3aa8179 output_thread: return from ao_play() if chunk->next is NULL
When the "next" chunk to be played is NULL, return from ao_play()
immediately, without going over the "while" loop (no-op).
2009-11-02 20:20:14 +01:00
Max Kellermann
408d52fe39 player_thread: check command before waiting during pause
While paused, the player thread re-locks its mutex and waits for a
signal.  This is racy: when the command is set while the thread is
waiting for the lock, it may wait forever.  This patch adds another
command check before player_wait().
2009-11-02 20:20:13 +01:00
Max Kellermann
b9013944dc output: signal the output thread when CANCEL is finished
After CANCEL, the output thread waits for another signal before it
continues playback, to synchronize with the caller.  There were some
situations where this signal wasn't sent properly.  This patch adds an
explicit g_cond_signal() at two code positions.
2009-11-02 19:09:25 +01:00
Max Kellermann
e814f8d5bd update: removed unused variable "update_notify"
That variable has been superseded by "remove_notify" (defined in
update_remove.c).
2009-11-02 17:22:41 +01:00
Max Kellermann
64a481d873 {decoder,player}_control: removed duplicate wakeups
Don't wake up the target thread in every iteration of the wait() loop.
Waking it up once, right after the command has been set, must be
enough.
2009-11-02 17:12:00 +01:00
Max Kellermann
93d8f9f00e player_control: lock player before setting seek parameters
These parameters must be protected with a mutex, too.  Wrap everything
inside player_lock()/player_unlock(), and use player_command_locked()
instead of player_command().
2009-11-02 17:02:05 +01:00
Max Kellermann
d1742a2330 output_thread: check command before g_cond_wait()
After CANCEL, call g_cond_wait() only if the new command is still
NONE.  Problem is that ao_command_finished() has to unlock the
audio_output object, and in the meantime, the player thread might have
submitted a new command.
2009-11-02 17:01:17 +01:00
Max Kellermann
5555d30bbd song_save: free song object on error
Fix a minor memory leak.
2009-11-01 17:51:32 +01:00
Max Kellermann
4b17aca747 song_save: load one song at a time
Changed songvec_load() to song_load().  Added start and end markers
for each song.  Removed the "key" line, it's redundant.
2009-11-01 17:51:29 +01:00
Max Kellermann
63dda94a02 database: save database format version 2009-11-01 17:50:01 +01:00
Max Kellermann
7f9ee00980 directory_save: partially revert the g_str_has_prefix() patch 2009-11-01 17:49:49 +01:00
Max Kellermann
53a749780a database: use strcmp() instead of g_str_has_prefix() 2009-11-01 15:54:06 +01:00
Max Kellermann
c504004702 database: removed redundant music_root allocation
The "music_root" global variable is allocated by db_init().
2009-11-01 15:51:19 +01:00
Max Kellermann
8bfe3497b0 replay_gain: trigger OPTIONS idle event on mode change 2009-11-01 15:44:56 +01:00
Max Kellermann
9bcfd3a47d text_file: allocate line buffers dynamically
Use a single GString buffer object in all functions loading the
database.  Enlarge it automatically for long lines.  This eliminates
the maximum line length for tag values.  There is still an upper limit
of 512 kB to prevent denial of service, but that's reasonable I guess.
2009-11-01 15:37:16 +01:00
Max Kellermann
451f932d80 directory_save: allocate directory object earlier, assign mtime
Allocate the directory object after the "directory:" line.  Assign the
mtime from the input file to this new object, instead of to the parent
directory.
2009-11-01 15:34:14 +01:00
Max Kellermann
10b7608926 directory_save: free directory on error
Fix a minor memory leak in the error handler.
2009-11-01 15:34:13 +01:00
Max Kellermann
2cd8a9fecf directory_save: abort on duplicate subdirectory
The old code tried to recover, but what's the point of that?  If a
directory is duplicate, something is wrong with the database file.
2009-11-01 15:34:13 +01:00
Max Kellermann
22279127f9 directory_save: moved code to directory_load_subdir() 2009-11-01 15:34:12 +01:00
Max Kellermann
ad01e1249b decoder_api: check decoder==NULL in decoder_read()
It's legal to pass decoder=NULL to decoder_read().  Add a check.
2009-11-01 15:34:12 +01:00
Max Kellermann
6ef428af2e decoder_control: removed the global variable "dc"
Allocate a decoder_control object where needed, and pass it around.
This will allow more than one decoder thread one day.
2009-10-31 19:22:56 +01:00
Max Kellermann
806496dfc9 Merge branch 'v0.15.x'
Conflicts:
	NEWS
	configure.ac
	src/decoder/ffmpeg_plugin.c
	src/update.c
2009-10-31 18:23:56 +01:00
Max Kellermann
9eadb517da player_thread: simplified thread destruction
Simply use "return" instead of g_thread_exit().
2009-10-31 18:01:05 +01:00
Max Kellermann
1ae4e4dcd3 songvec: sort songs by album name first, then disc/track number
When the songs of two albums are in the same directory, all songs of
an album should be right next to each others.
2009-10-31 17:36:56 +01:00
Max Kellermann
d099a7e464 songvec: simplified compare_tag_item()
Moved some code to tag_get_value_checked(), to eliminate several NULL
checks.
2009-10-31 17:32:00 +01:00
Max Kellermann
25a806a347 player_control: protect command, state, error with a mutex
Use GMutex/GCond instead of the notify library.  Manually lock the
player_control object before accessing the protected attributes.  Use
the GCond object to notify the player thread and the main thread.
2009-10-31 17:02:12 +01:00
Max Kellermann
73cff374fd {player,output}_thread: fixed elapsed_time quirks
Right after seeking and song change, the elapsed_time shows old
information, because the output thread didn't finish a full chunk
yet.  This patch re-adds a second elapsed_time variable, and keeps
track of a fallback value, in case the output thread can't provide a
reliable value.
2009-10-30 16:28:15 +01:00
Max Kellermann
cec019efff output_thread: return bool from ao_play()
Return false when there was no chunk in the pipe.  If the function
returns true, then audio_output_task() will not wait for a notify from
the player thread.  This fixes a race condition.
2009-10-29 23:35:27 +01:00
Max Kellermann
4748decd8d player_thread: set error status in play_next_chunk()
Don't set the error in play_chunk(); do all the error handling in the
caller.  The errored_song attribute isn't set anymore; it doesn't make
sense for PLAYER_ERROR_AUDIO.
2009-10-29 22:39:48 +01:00
Max Kellermann
975143ab47 output_control: fixed deadlock in audio_output_update()
Call a version of audio_output_close() which doesn't lock recursively.
2009-10-29 22:39:42 +01:00
Viliam Mateicka
bb5acc939f httpd: add config option to limit number of clients 2009-10-29 22:38:18 +01:00
Max Kellermann
bde3d14339 output: consistently lock audio output objects
Always keep the audio_output object locked within the output thread,
unless a plugin method is called.  This fixes several race conditions.
2009-10-29 17:06:40 +01:00
Max Kellermann
1403172ef3 output_plugin: added method "drain"
drain() is the opposite of cancel(): it waits until all data in the
buffer has finished playing.  Instead of implicitly draining in the
close() method like the ALSA plugin has been doing it forever, let the
output thread decide whether to drain or to cancel.
2009-10-29 15:59:40 +01:00
Max Kellermann
f74ee1a352 output/alsa: don't recover on CANCEL
The recovery is for nothing if we get CLOSE afterwards.  Let's not
recover in the cancel() method, and let the next play() call sort it
out.
2009-10-29 15:59:35 +01:00
Max Kellermann
cf8d278b78 output_thread: removed redundant filter_close() call
Don't call filter_close() right after ao_close().
2009-10-29 15:58:21 +01:00
Max Kellermann
82af161210 output/pulse: initialize pulse_output.mixer
This variable was uninitialized and led to crashes.
2009-10-29 15:58:18 +01:00
Max Kellermann
9fed97b1f1 decoder/ffmpeg: removed the "author" vs "artist" workaround
libavformat gives us the song artist in the "author" field.  Since we
use av_metadata_conv(), we don't need to check for "artist".
2009-10-28 22:17:10 +01:00
Alam Arias
83a9cf74fb include pulse/version.h for PA_CHECK_VERSION 2009-10-27 07:46:53 +01:00
Viliam Mateicka
a13e9832e7 encoder: renaming none_encoder to null_encoder 2009-10-26 20:02:34 +01:00
Viliam Mateicka
f67426871b mpd.conf: new bool config value for enabling/disabling inotify update 2009-10-26 19:14:40 +01:00
Viliam Mateicka
2bfddd4310 encoder: new encoder plugin which just pass data through 2009-10-24 19:01:15 +02:00
Viliam Mateicka
7b343eaf50 add --disable-inotify for configure to disable inotify when autodetected 2009-10-24 18:24:29 +02:00
Max Kellermann
ac830468bf output/jack: implement methods enable()/disable()
Don't connect to JACK before MPD has daemonized.
2009-10-23 10:56:25 +02:00
Max Kellermann
7547b1170b output/pulse: implement methods enable()/disable()
Don't connect to PulseAudio before MPD has daemonized.
2009-10-23 10:56:16 +02:00
Max Kellermann
e53ca368a5 output_plugin: added methods enable() and disable()
With these methods, an output plugin can allocate some global
resources only if it is actually enabled.  The method enable() is
called after daemonization, which allows for more sophisticated
resource allocation during that method.
2009-10-23 10:55:52 +02:00
Max Kellermann
c426a0bc5c output/pulse: call mixer on state changes
Don't let the mixer plugin "override" the libpulse callbacks.
Instead, add a "mixer" attribute to the pulse_output struct, and call
the mixer on all interesting events.
2009-10-23 10:33:26 +02:00
Max Kellermann
acc99da73d mixer_control: don't close the mixer if set_volume() fails
A mixer is useful enough if it can be read.  Setting it may be
found unavailable at runtime.
2009-10-23 10:33:23 +02:00
Max Kellermann
c4f895daf4 mixer_plugin: get_volume() may return -1 if unavailable
If the method get_volume() returns -1 and no error object is set, then
the volume is currently unavailable, but the mixer should not be
closed immediately.
2009-10-23 10:32:25 +02:00
Max Kellermann
ede828c910 mixer_control: allow methods "open" and "close" to be NULL
It's possible to have a mixer implementation which does not explicitly
need the methods open() and close().
2009-10-23 09:15:51 +02:00
Max Kellermann
7dd172efec mixer/pulse: don't get volume if stream is not yet ready
Moved the check from pulse_mixer_open() to pulse_mixer_update().
2009-10-22 20:28:12 +02:00
Max Kellermann
80ac82c8fb output/fifo: renamed source to fifo_output_plugin.c 2009-10-22 19:23:30 +02:00
Max Kellermann
fd182f6d1e Makefile.am: enable the "subdir-objects" option
Don't clutter the top directory with *.o files.
2009-10-22 18:23:22 +02:00
Max Kellermann
a05d0d5d94 audio: removed function finishAudioConfig()
There's no point in clearing the audio format before exiting.
2009-10-22 17:12:32 +02:00
Max Kellermann
0107ef2aad main: put #ifdef inside winsock_init()
This way, the function call in the main() function does not need
another pair of #ifdef/#endif.
2009-10-22 17:12:28 +02:00
SF Markus Elfring
a153f21315 permission: improve const-correctness 2009-10-22 17:11:39 +02:00
Max Kellermann
294aaf7a90 playlist: new ASX playlist plugin
Based on the XSPF playlist plugin.
2009-10-21 23:39:47 +02:00
Max Kellermann
2024763d2a playlist/xspf: ignore text in root, playlist, tracklist
Added a missing "break".
2009-10-21 23:27:05 +02:00
Max Kellermann
9526fdbe73 audio_format: wildcards allowed in audio_format configuration
An asterisk means that this attribute should not be enforced, and
stays whatever it used to be.  This way, some configuration values
work like masks.
2009-10-21 23:01:04 +02:00
Max Kellermann
a5c4566fa1 audio_parser: moved code to separate functions 2009-10-21 23:01:03 +02:00
Max Kellermann
2c05430002 output: convert config_audio_format to an audio_format struct
This allows more sophisticated audio format selection.
2009-10-21 22:37:28 +02:00
Max Kellermann
643650dba7 audio_parser: renamed parameter "error" to "error_r"
It's a double pointer.
2009-10-21 22:22:23 +02:00
Max Kellermann
8cb6854da4 audio: removed config_param.value NULL check
The value is always non-NULL.
2009-10-21 22:22:20 +02:00
Max Kellermann
83844ec239 output/jack: make ringbuffer_size a size_t 2009-10-21 21:39:26 +02:00
Max Kellermann
1bfd25be35 output/jack: connect to server on MPD startup
.. and keep up the JACK connection while MPD runs.  Allocate the ring
buffers on the first open, and free them at MPD exit.
2009-10-21 21:37:11 +02:00
Max Kellermann
cee216f2dc output/jack: removed the empty "cancel" method
JACK doesn't need cancel() because it won't do much anyway.  Buffers
are small.
2009-10-21 21:01:00 +02:00
Max Kellermann
27c246e8d9 output/jack: renamed parameter "error" to "error_r"
It's a double pointer.
2009-10-21 20:13:39 +02:00
Max Kellermann
1ff39476eb output/jack: implement the "pause" method
Don't disconnect from JACK during pause.
2009-10-21 18:33:05 +02:00
Max Kellermann
acfd9a73bc output/jack: renamed source to jack_output_plugin.c 2009-10-21 18:33:01 +02:00
Max Kellermann
b479a264b6 pulse: code rewrite using the asynchronous libpulse API
This is a complete rewrite of the PulseAudio output plugin.  It uses
the asynchronous API, which gives us more control over everything.
Additionally, it connects to the PulseAudio server on startup, and
keeps this connection up while MPD runs.  During pause, instead of
closing the stream, it enables "cork".
2009-10-21 10:30:42 +02:00
Max Kellermann
ac32f36e4e mixer_plugin: pass audio_output pointer to mixer_plugin.init()
This allows the mixer object to access its associated audio output
object.
2009-10-21 09:48:41 +02:00
Max Kellermann
b8ccc885c8 volume: added PIPE_EVENT_MIXER
Flush the hardware volume cache, and send the MIXER idle event.  This
allows mixer plugins to detect volume changes.
2009-10-21 09:48:37 +02:00
Max Kellermann
4e2fb3fb89 mixer_plugin: use GError for error handling 2009-10-20 22:10:56 +02:00
Max Kellermann
9cd2129eeb output_init: renamed parameter "error" to "error_r"
It's a double pointer.
2009-10-20 21:26:28 +02:00
Max Kellermann
097e200a97 mixer/{oss,alsa}: renamed the mixer source files 2009-10-20 21:23:05 +02:00
Max Kellermann
bd28caed3c output/pulse: renamed context to "Music Player Daemon"
This looks nicer in the PulseAudio manager than just "mpd".
2009-10-20 21:07:38 +02:00
Max Kellermann
bc4266bef8 pulse: renamed source files 2009-10-20 21:05:11 +02:00
Max Kellermann
bc629c8a3e pulse: announce "media.role=music"
This allows PulseAudio to do some advanced tweaks.
2009-10-20 21:02:10 +02:00
Max Kellermann
2e9e34aa40 main: call g_set_application_name()
I'm not sure about the advantages of calling g_set_application_name(),
because I don't use a task manager (except for ps and kill), but it
sure doesn't hurt.
2009-10-20 21:02:00 +02:00
Max Kellermann
c953d6409d mapper, update, ...: use g_build_filename(), G_DIR_SEPARATOR, ...
Try to be as portable as possible, use GLib path name functions and
macros.
2009-10-20 21:01:55 +02:00
Max Kellermann
447e4d3583 Merged release 0.15.5 from branch 'v0.15.x'
Conflicts:
	NEWS
	configure.ac
	src/decoder/flac_plugin.c
	src/update.c
2009-10-18 19:02:43 +02:00
Serge Ziryukin
33bf6c0978 decoder/vorbis: avoid unused symbol warnings from vorbisfile.h
svn r13289 of libvorbis introduced static callbacks (like OV_CALLBACKS_DEFAULT)
defined in "vorbisfile.h" header. First released version with this change is libvorbis-1.2.2.
In libversion-1.2.3 OV_EXCLUDE_STATIC_CALLBACKS define was added to avoid
warnings about unused static callbacks. Information on the OV_EXCLUDE_STATIC_CALLBACKS
can be found in http://svn.xiph.org/trunk/vorbis/CHANGES.
2009-10-18 18:35:02 +02:00
Max Kellermann
d69e0ab53a command: omitting the range end is possible
When the range end is missing, then the maximum possible value is
assumed.
2009-10-18 01:24:49 +02:00
Max Kellermann
e7c267db4f command: use GLib limit macros in check_range()
These are portable and don't require limits.h.
2009-10-18 01:24:45 +02:00
Max Kellermann
6a5e7c118b doc/protocol.xml: documented range parameter in "delete" 2009-10-18 01:24:22 +02:00
Max Kellermann
bddb6b4273 command: allow changing replay gain mode on-the-fly
The new command "replay_gain_mode" allows the user to switch the
replay gain mode on-the-fly.  No more mpd.conf editing.
2009-10-17 22:58:19 +02:00
Max Kellermann
7ec32704f9 replay_gain: moved mode parser to replay_gain_set_mode_string() 2009-10-17 22:40:50 +02:00
Max Kellermann
8d217567c6 replay_gain: added setting "replaygain off"
There was no setting for disabling replay gain.  It was off when the
"replaygain" setting was not there.
2009-10-17 21:38:45 +02:00
Max Kellermann
a17d814381 replay_gain: read configuration even when replay gain is disabled
It will be possible to enable replay gain at runtime even when it is
disabled in the configuration file.  This patch enables the preamp
settings in this case.
2009-10-17 21:38:32 +02:00
Max Kellermann
5554633ab8 update: obey .mpdignore files 2009-10-16 18:11:43 +02:00
Max Kellermann
8cd845b79e doc/user.xml: added chapter "Using MPD" 2009-10-16 18:10:36 +02:00
Max Kellermann
06da91a73b doc/user.xml: added playlist plugin reference 2009-10-16 17:51:33 +02:00
A. Klitzing
792010ba32 notify_queue: use g_timeout_add_seconds() 2009-10-15 21:22:39 +02:00
Max Kellermann
b69246c646 player_thread: don't call audio_output_all_check() if paused
When the audio output fails to open, MPD pauses playback, but doesn't
reset player.play_audio_format.  This leads to an assertion failure in
audio_output_all_check() on the next REFRESH command, because no audio
output is open.
2009-10-15 20:47:00 +02:00
Qball Cow
f4ea9b7393 Add PLS Parser 2009-10-15 00:08:06 +02:00
Max Kellermann
1ff101c568 input/lastfm: removed obsolete last.fm input plugin
This has been replaced by the last.fm playlist plugin.  The input
plugin has never worked well, and was just a playground to experiment
with the last.fm radio protocol.
2009-10-13 19:43:56 +02:00
Max Kellermann
a93ffdd1be command: "load" supports remote playlists (m3u, xspf, lastfm://)
This patch integrates the playlist plugin API to the MPD core.  We'll
be able to do much more in the future with that API, that's just the
beginning.
2009-10-13 18:53:33 +02:00
Max Kellermann
319149254d main: initialize playlist plugins 2009-10-13 18:01:11 +02:00
Max Kellermann
f7ce4f6239 song: renamed attribute "url" to "uri" 2009-10-13 18:01:06 +02:00
Max Kellermann
28442cce9f queue: no CamelCase
Renamed idToPosition.
2009-10-13 16:43:06 +02:00
Max Kellermann
f122e6d456 playlist: added plugin for last.fm radio
This plugin will replace the last.fm input plugin, once the playlist
API is integrated into MPD.
2009-10-13 16:32:10 +02:00
Max Kellermann
8391ac4cc8 playlist: added XSPF plugin 2009-10-13 16:31:07 +02:00
Max Kellermann
898d885ae2 test/dump_playlist: try playlist_list_open_uri() first 2009-10-13 16:25:37 +02:00
Max Kellermann
a61d0c9567 Merge branch 'v0.15.x' 2009-10-13 16:25:17 +02:00
Max Kellermann
eea4edd92c test/dump_playlist: parse a configuration file 2009-10-13 16:20:21 +02:00
Max Kellermann
cb331ae436 playlist_list: pass configuration to playlist plugins
This patch completes the configuration support.
2009-10-13 16:19:21 +02:00
Max Kellermann
767e27c8f0 playlist/m3u: added plugin name 2009-10-13 16:13:36 +02:00
Max Kellermann
e78370e050 playlist_list: initialize the "playlist" variable
Prevent access on uninitialized variable if the plugin list is empty.
2009-10-13 16:13:33 +02:00
Max Kellermann
ea616b3ed4 tag: removed the "_ITEM_" suffix from the enum names 2009-10-13 16:12:45 +02:00
Max Kellermann
a9dc0e816c state_file: use g_timeout_add_seconds() 2009-10-13 16:12:44 +02:00
Max Kellermann
4390d72b14 configure.ac: require GLib 2.16
Accidently, MPD has been using several GLib 2.16 functions for a
while, and nobody noticed yet.  To simplify the code base, let's bump
the minimum GLib version for MPD to 2.16.  That version is old enough,
and it's reasonable to expect users to have it.
2009-10-13 16:12:43 +02:00
Max Kellermann
2bf740fc71 playlist_plugin: new plugin API for playlist parsers
Based on this API, we will add parsers for EXTM3U, PLS, ASX, last.fm
radio and others.

There is no integration into the MPD core yet.  Right now, we have a
command line test program.  This is work in progress.
2009-10-12 22:34:04 +02:00
Max Kellermann
dbb1e732b8 text_input_stream: input_stream wrapper for reading text files 2009-10-12 22:30:59 +02:00
Max Kellermann
7ec503c4ec song: moved code to song_update.c
Moved all the code which depends on the decoder plugins to a separate
source.  That allows leaner test programs.
2009-10-12 22:30:50 +02:00
Max Kellermann
a07ab27dae decoder_thread: removed redundant NULL assignments
The while() clause resets the "plugin" variable.  We don't need to
reset it at the end of the loop body.
2009-10-11 23:44:21 +02:00
Max Kellermann
727c301fbc input_stream: use "goffset" instead of "off_t"
The "off_t" type may change when you enable or disable large file
support on 32 bit platforms.  This caused severe ABI problems within
MPD when we enabled LFS for the first time: two sources included
config.h and sys/types.h in different order, and had different off_t
sizes - leading to memory corruption because of ABI incompatibility.
This patch attempts to get rid of all public "off_t" uses: it removes
"off_t" from the input_stream ABI/API, and switches to GLib's 64 bit
"goffset" type.  This may hurt 32 bit embedded platforms a tiny bit,
but that's not even measurable.
2009-10-11 23:32:22 +02:00
Max Kellermann
71f881d5cb Merge branch 'v0.15.x'
Conflicts:

	NEWS
	configure.ac
2009-10-11 23:25:27 +02:00
Avuton Olrich
d4e3fb4330 configure: Fix up the recorder plugin configure. 2009-10-10 11:36:22 -07:00
Max Kellermann
57f69a2915 doc/protocol.xml: "addid" with negative position is deprecated 2009-10-09 10:18:02 +02:00
Max Kellermann
448aefaace player_thread: get "elapsed" from audio outputs
Tracking the "elapsed" time from the chunks which we have sent to the
output pipe is very imprecise: since we have implemented the music
pipe, we're sending large number of chunks at once, giving the
"elapsed" time stamp a resolution of usually more than a second.

This patch changes the source of this information to the outputs.  If
a chunk has been played by all outputs, the "elapsed" time stamp is
updated.

The new command PLAYER_COMMAND_REFRESH makes the player thread update
its status information: it tells the outputs to update the chunk time
stamp.  After that, player_control.elapsed_time is current.
2009-10-08 22:09:25 +02:00
Max Kellermann
47b5e73a15 player_thread: always clear player_control.next_song on return
pc.next_song might be non-NULL even if player.queued==true: when the
decoder has started decoding the next song, but the result hasn't been
read yet.
2009-10-08 22:09:25 +02:00
Max Kellermann
d6a6f428b3 player_control: eliminate PLAYER_COMMAND_PLAY
Sending PLAYER_COMMAND_STOP followed by PLAYER_COMMAND_QUEUE does the
same.  PLAYER_COMMAND_PLAY is redundant.
2009-10-08 21:22:31 +02:00
Max Kellermann
fd3934b849 player_control: added several assertions on pc.next_song==NULL
After some of the commands, the player thread must have reset the
pc.next_song attribute.
2009-10-08 21:17:00 +02:00
Max Kellermann
e5857cb722 player_control: no CamelCase 2009-10-08 21:12:57 +02:00
Max Kellermann
2ec89c6304 player_control: clear errored_song in clearPlayerError()
Without the player error code, it errored_song variable is not used,
and should be cleared, to avoid invalid access in pc_song_deleted().
2009-10-08 21:00:16 +02:00
Max Kellermann
76953a9748 player_control: bundle "get" functions in pc_get_status()
The new player_status struct replaces a bunch of playerGetX()
functions.  When we add proper locking to the player_control struct,
we will only need to lock once for the "status" command.
2009-10-08 20:48:07 +02:00
Max Kellermann
128a5fa4a5 player_control: allocate getPlayerErrorStr() result
This lets us eliminate the static fixed-size buffer.
2009-10-08 20:45:38 +02:00
Max Kellermann
a5960c20cc playlist_control: "previous" really plays the previous song
No more CD player emulation.  The current behaviour of "previous" is
difficult for a client to predict, because it does not definitely know
the current position within the song.  If a client wants to restart
the current song, it can always send "playid".
2009-10-08 20:33:50 +02:00
Max Kellermann
aa71ce4cd5 input_stream: include config.h for AC_SYS_LARGEFILE macros
AC_SYS_LARGEFILE defines macros for config.h.  If we don't include
config.h, we don't get large file support.
2009-10-08 16:57:55 +02:00
Max Kellermann
16c981d425 decoder_api: document all function parameters 2009-10-08 15:39:45 +02:00
Alam Arias
81e56705ad configure.ac: build with large file support by default
This fixes mpg123 support.
2009-10-08 15:24:59 +02:00
Max Kellermann
ecb118f1ed state_file: save only if something has changed
If nothing has changed since the last save, don't save the state
file.  Saving will spin up the hard drive, which is undesirable on
hosts where MPD is idling in background.
2009-10-08 15:22:39 +02:00
Max Kellermann
1e663b1869 output_state: no CamelCase 2009-10-06 10:30:10 +02:00
Max Kellermann
7013f9fc31 Merged release 0.15.4 from branch 'v0.15.x'
Conflicts:
	NEWS
	configure.ac
2009-10-03 16:17:02 +02:00
Max Kellermann
31cabc751d command: range support for "delete" 2009-09-30 23:13:13 +02:00
Max Kellermann
0478a8e288 playlist_edit: moved code to playlist_delete_internal() 2009-09-30 23:10:15 +02:00
Tony
1039d57251 Add the sidplay filter param to doc/mpdconf.example. 2009-09-28 11:59:55 +02:00
Tony
934a38f976 Make the sidplay decoder filter configurable. 2009-09-28 11:59:55 +02:00
Max Kellermann
8f261af5c1 automatically update the database with Linux inotify
This patch implements a light-weight inotify library, and watches all
directories below the music directory.  It updates all directories
where files changed after a delay of 5 seconds.
2009-09-25 18:32:00 +02:00
Max Kellermann
3e8bdb9384 update: added missing stdbool.h include 2009-09-25 16:43:33 +02:00
Max Kellermann
7542ec4f20 command: relax requirements for unquoted words
Allow most printable characters in unquoted words.  The tokenizer
patch introduced very strict requirements for command parameters -
those were undocumented, and we're reverting the strictness now.
2009-09-25 00:53:15 +02:00
Max Kellermann
89ba540e6d command: added command "rescan"
"rescan" is the same as "update", but it discards existing songs in
the database.
2009-09-24 21:55:40 +02:00
Max Kellermann
47ab2ad6f3 configure.ac: rename HAVE_CURL to ENABLE_CURL 2009-09-24 21:40:07 +02:00
Max Kellermann
06d5d4b03e conf: handle fatal errors with GError
Don't call g_error(), which will abort the process and dump core.

This patch does not affect all the config_get_X() functions.  These
need some more refactoring.
2009-09-24 21:40:07 +02:00
Max Kellermann
f3739a73af conf: splitted function config_param_free() 2009-09-24 21:40:05 +02:00
Max Kellermann
1b227e0145 cmdline: handle fatal errors with GError
Don't call g_error(), which will abort the process and dump core.
2009-09-24 21:40:05 +02:00
Max Kellermann
308b3f2337 listen: handle fatal errors with GError
Don't call g_error(), which will abort the process and dump core.
2009-09-24 21:40:04 +02:00
Max Kellermann
1e56107967 update: splitted update.c into several sources 2009-09-24 21:39:46 +02:00
Max Kellermann
b0e1a3d34c update: pass const string to update_enqueue()
Duplicate the path string within update.c, do not expect an allocated
string as parameter.
2009-09-24 21:39:43 +02:00
Max Kellermann
1cc4914b24 update: renamed directory_update_init() to update_enqueue() 2009-09-24 17:57:39 +02:00
Max Kellermann
aec18c74ec decoder/sidplay: free songlength data blob in error handler
When parsing the songlength database fails, the code forgot to free
the memory allocated by the raw file data.
2009-09-24 10:17:58 +02:00
Max Kellermann
4729d10bb2 decoder/sidplay: moved code to sidplay_load_songlength_db() 2009-09-24 10:14:05 +02:00
Max Kellermann
a8af3ce0dd decoder/sidplay: free GError objects
The caller is responsible fro freeing GError objects.  That
g_error_free() call was missing in two places.
2009-09-24 10:05:21 +02:00
Max Kellermann
d657be33ba decoder/sidplay: pass GError** to g_file_get_contents()
The error handler dereferences GError*, but did not retrieve that
object from g_file_get_contents().
2009-09-24 10:04:24 +02:00
Max Kellermann
a86f9b8035 cmdline: removed options --create-db and --no-create-db
Both options are deprecated, and should not be used anymore.  Many
users get confused by their presence.
2009-09-20 23:31:35 +02:00
Patrik Weiskircher
32f212cb86 output/osx: fix the OS X 10.6 build
Include CoreServices/CoreServices.h.
2009-09-20 23:30:37 +02:00
Max Kellermann
fdc479676f Merge branch 'v0.15.x'
Conflicts:
	NEWS
	configure.ac
	doc/mpdconf.example
2009-09-10 23:18:43 +02:00
Max Kellermann
4cdf62000c INSTALL: mention libwavpack and OpenAL 2009-09-09 10:44:12 +02:00
Serge Ziryukin
761771ad24 output/openal: support OpenAL plugin on Mac OS X 2009-09-07 15:52:32 +02:00
Serge Ziryukin
f5f4a9da6b doc: documented the "openal" output plugin settings 2009-09-06 22:22:17 +02:00
Serge Ziryukin
eefef369ea output/openal: fix default device name 2009-09-06 22:22:17 +02:00
Patrik Weiskircher
4ebf53ffdf configure.ac: use /Developer/SDKs/MacOSX10.5.sdk
First, this is not a nice fix. I am also not sure why the error
happens in the first place. I assume Apple deprecated some stuff in
10.6 + x86_64.

My patch simply uses the 10.5 SDK if compiling on 10.6 Snow Leopard -
which is installed by default if you install XCode 3.2 that comes with
Snow Leopard. The reason this is not a nice fix is, of course, that
this doesn't fix the problem. It just "postpones" it to the next
release of MacOS X. But, some people may need it, and its better than
nothing.
2009-09-06 17:42:50 +02:00
Serge Ziryukin
8b6a5d19d0 openal output plugin 2009-09-06 17:34:56 +02:00
Max Kellermann
129920e8f4 Merge branch 'master' of git://git.musicpd.org/avuton/mpd 2009-09-02 12:51:44 +02:00
Avuton Olrich
dc3b96a003 doc: Unify position of footnotes and cleanup wording. 2009-08-30 18:57:12 -07:00
Avuton Olrich
5a0d77bea3 doc: Remove superfluous comment already described in footnotes. 2009-08-30 18:53:14 -07:00
Mike Dawson
430b5b0490 decoder/sidplay: support seeking 2009-08-30 19:49:16 +02:00
Mike Dawson
cde9408bd8 decoder/sidplay: implemented songlength database
[mk: added autoconf test; fixed songlen_data_size type]
2009-08-30 19:49:04 +02:00
Mike Dawson
85ce9aa7de decoder/sidplay: subtunes 2009-08-30 19:48:56 +02:00
Max Kellermann
f2ff2409ad Merged release 0.15.3 from branch 'v0.15.x'
Conflicts:

	NEWS
	configure.ac
2009-08-30 09:42:12 +02:00
Max Kellermann
bff4c54ece decoder/mpg123: new decoder plugin based on libmpg123
Still missing:
- seeking
- tags
- streaming
- encodings other than MPG123_ENC_SIGNED_16
2009-08-26 20:08:13 +02:00
Anton Khirnov
df0c26a394 command: add "findadd" command. 2009-08-25 13:43:22 +02:00
Gunnar Roth
1e56c7b862 decoder/vorbis: open file in "binary" mode
fopen change for win32 in tag dup of vorbis
win32 adaption for head changes
2009-08-25 00:49:48 +02:00
Max Kellermann
4a0d4a02a6 output/recorder: new output plugin for recording radio streams
The recorder plugin writes audio played by MPD to a file.  This may be
useful for recording radio streams.

This implementation is incomplete, because support for tags is
missing, and MPD should be able to record each track to a different
file.
2009-08-24 18:57:06 +02:00
Max Kellermann
4231ec51c3 conf: removed the deprecated "error_file" option
This option was deprecated by the 0.15 release.  This patch makes this
option invalid.
2009-08-24 17:12:15 +02:00
Max Kellermann
f401c1059c Merged release 0.15.2 from branch 'v0.15.x'
Conflicts:

	NEWS
	configure.ac
2009-08-15 21:18:38 +02:00
Max Kellermann
e28a0e97b5 decoder_control: protect command, state with a mutex
Replace decoder_control.notify with decoder_control.mutex and
decoder_control.cond.  Lock the mutex on all accesses to
decoder_control.command and decoder_control.state.
2009-08-13 23:33:46 +02:00
Mike Frysinger
499ed62dd7 use daemon() when the C library provides it
For systems that cannot support fork() (like no-mmu Linux), use daemon() if
it is available for the daemonizing code.

Signed-off-by: Mike Frysinger <vapier@gentoo.org>
2009-08-13 17:25:30 +02:00
Max Kellermann
c3e02bec3b test: initialize locale in read_tags.c 2009-08-04 00:41:06 +02:00
Max Kellermann
4363c49443 tag_id3: moved id3_genre_name() call to tag_id3_import_text()
The ID3_FRAME_GENRE field is queried only in tag_id3_import_text().
Don't pass the tag type to import_id3_string().
2009-08-04 00:15:46 +02:00
Max Kellermann
09571fcc55 tag_id3: splitted function tag_id3_import_frame()
Splitted tag_id3_import_frame() into two specialized functions:
tag_id3_import_text() and tag_id3_import_comment().  Use
id3_frame_field() instead of directly accessing id3_frame.fields.
2009-08-04 00:15:43 +02:00
Max Kellermann
c7563a5783 tag_id3: corrected parameter types
Changed "int type" to "enum tag_type".  Converted "int is_id3v1" to
"bool".
2009-08-04 00:14:24 +02:00
Max Kellermann
b06b4f937d tag_id3: converted tag_is_id3v1() to an inline function
Prefer C over CPP.
2009-08-04 00:13:22 +02:00
Max Kellermann
86c6bb618b tag_id3: no CamelCase
Renamed functions.
2009-08-04 00:13:22 +02:00
Anton Khirnov
bff72634ca ffmpeg_plugin: convert metadata to generic format 2009-08-03 17:40:22 +02:00
Max Kellermann
6f060081be command: moved command_process_list() to client.c 2009-07-29 08:00:01 +02:00
Courtney Cavin
7f865f722c playlist: CamelCaseIsBad
Renamed all playlist functions to non-CamelCase.
2009-07-28 18:07:01 -04:00
Courtney Cavin
614a011845 input/lastfm: Ensure multiple identical xml entities are decoded.
Previously, if two identical entities appeared in one string, only the
first would get decoded. This fixes that bug.
2009-07-28 16:40:52 -04:00
Max Kellermann
0c66832b3b doc: removed deprecated mixer options from manpage
Removed documentation about the global options mixer_type,
mixer_device and mixer_control.
2009-07-28 17:22:55 +02:00
Max Kellermann
f78366910e client: splitted client.c into several pieces
The soure file client.c has nearly 1000 lines, time for splitting it
into smaller pieces to improve readability.
2009-07-28 17:17:23 +02:00
Max Kellermann
c426bbcf95 client: moved struct client to client_internal.h
Prepare splitting client.c into several sources.
2009-07-28 16:42:40 +02:00
Courtney Cavin
6d71094ce5 input/lastfm: use metadata
Added a patch to flush out the last.fm input plugin slightly. It
basically turns it into a wrapper for the appropriate plugin. Most
notably metadata is now extracted.
2009-07-28 16:41:50 +02:00
Max Kellermann
9322f04529 cmdline: obey $(sysconfdir) for default mpd.conf location
Instead of hard-coding the path "/etc/mpd.conf", use the configured
$(sysconfdir) path.  This can be set with:

 ./configure --sysconfdir=/etc

Note that this changes the default path to "/usr/local/etc/mpd.conf",
given the default prefix "/usr/local".  This is actually more correct
than the old default.
2009-07-28 16:17:18 +02:00
Max Kellermann
884201b919 client: moved some code to client_list_X()
Make the client list management a separate sub-library.
2009-07-23 19:20:30 +02:00
Max Kellermann
a0afd0369f client: don't include socket headers
The client code uses portable GLib I/O functions and doesn't need the
OS specific socket headers.
2009-07-23 17:32:12 +02:00
Max Kellermann
caf48ee973 player_thread: don't use precalculated size_to_time
Calculate the total play time with the audio_format object each time,
using audio_format_time_to_size().  The function
audioFormatSizeToTime() is not needed anymore, and will be removed
with this patch.
2009-07-23 12:27:05 +02:00
Max Kellermann
0749dbfadf player_thread: moved code to update_song_tag() 2009-07-23 12:26:26 +02:00
Max Kellermann
54889c72e3 pcm_convert: use GError for error handling
Don't abort the whole MPD process when the conversion fails.  This has
been a denial-of-service attack vector for years.
2009-07-23 12:01:03 +02:00
Max Kellermann
cba126ceb8 pcm_resample_fallback: don't include glib.h
This library does not use GLib directly.
2009-07-22 19:54:57 +02:00
Max Kellermann
c95663312a pcm_resample_fallback: removed G_GNUC_UNUSED attribute 2009-07-22 19:52:25 +02:00
Max Kellermann
00ee3de7b2 pcm_channels: num_channels is unsigned
You cannot have a negative number of channels, let's pass it as
uint8_t instead of int8_t.
2009-07-22 19:12:04 +02:00
Max Kellermann
172a1dbdb9 pcm_byteswap: converted NULL checks to assertions
It is illegal to pass a NULL buffer to pcm_byteswap_X().  The result
of this is that pcm_byteswap_X() never returns NULL.
2009-07-22 19:04:38 +02:00
Max Kellermann
c5a662f405 pcm_convert: added pcm_convert_state.byteswap_buffer
Currently, byteswapping is performed on the format_buffer.  This can
go wrong when this buffer is used twice during one run.  Add a
separate buffer for swapping the byte order.
2009-07-22 15:56:56 +02:00
Max Kellermann
9277950441 filter/volume: check the flag audio_format.reverse_endian
The volume plugin does not work for reverse_endian samples.
2009-07-22 15:56:50 +02:00
Max Kellermann
44c97a8f6d audio_format: added API documentation 2009-07-22 15:56:48 +02:00
Max Kellermann
6a071efa27 audio_format: initialize reverse_endian in audio_format_init()
This line was missing in the reverse_endian patch, and led to
undefined values and crashes in that attribute.
2009-07-22 15:56:36 +02:00
Michal Nazarewicz
bfed1c04cc daemon: daemonize_close_stdin() optimised.
Changed function to first close standard input (this may
fail but we don't care) and then try to open /dev/null (this
may fail but it shouldn't on Unix platforms plus we don't
know what to do in such case anyways).  Since standard input
has the "zeroth" descriptor number next "open" will use it.

Since there is no "/dev/null" on Windows (It's not even
a valid path!) the second step is skipped if WIN32 is
defined.

As a final touch, since the function consists of merely two
function calls it has been moved to header file and declared
static inline.

[mk: un-inline daemonize_close_stdin()]
2009-07-22 13:43:21 +02:00
Eugeny N Dzhurinsky
bdb1965b50 cmdline: renamed options.stderr to options.log_stderr
On FreeBSD, "stderr" is a macro, and using this name for a struct
member breaks the build.

[mk: renamed _stderr to log_stderr]
2009-07-22 13:40:19 +02:00
Max Kellermann
c9d43b4d71 Merge branch 'master' of git://git.infradead.org/users/dwmw2/mpd
Conflicts:

	Makefile.am
2009-07-19 18:18:32 +02:00
David Woodhouse
49ede85827 Support wrong-endian ALSA output 2009-07-19 17:15:35 +01:00
David Woodhouse
05693e2d5d Add reverse_endian field to struct audio_format and handle conversion 2009-07-19 17:15:21 +01:00
Max Kellermann
c5ec035fb4 tag_ape: simplified the apeItems array
Make "enum tag_type" the array index, and convert apeItems to a sparse
array.
2009-07-19 18:04:42 +02:00
Max Kellermann
5e2f98fdad tag_ape: moved code to tag_ape_import_item()
Improve code readability.
2009-07-19 17:59:36 +02:00
Max Kellermann
7b92750622 tag_ape: converted apeItems and tagItems to global vars
Don't initialize those arrays each time tag_ape_load() is called.
2009-07-19 17:59:35 +02:00
David Woodhouse
37754559b8 Add audio_format_init() function
It makes no difference right now, but we're about to add an endianness
flag and will want to make sure it's correctly initialised every time.
2009-07-19 16:54:11 +01:00
Max Kellermann
09008cb0ec client: return "enum command_return" instead of "int"
Several functions work with the wrong return type, this patch fixes
them.
2009-07-19 15:53:48 +02:00
Max Kellermann
b58aa1f5ee removed buffer2array.c 2009-07-19 15:15:41 +02:00
Max Kellermann
38e9205d4e command: use the tokenizer library 2009-07-19 15:15:36 +02:00
Max Kellermann
16ff44ad30 conf: use the tokenizer library 2009-07-19 15:11:37 +02:00
Max Kellermann
1745e68795 tokenizer: new library replacing buffer2array()
The new code is more robust and more flexible.  It provides detailed
error information in GError objects.
2009-07-19 15:11:36 +02:00
Michal Nazarewicz
809c96b53f daemon: Moved empty Windows version functions to header file
On Windows only daemonize_close_stdin() function does
something.  Other functions are either empty or generate an
error.  Those have been moved to header file and declared
static inline so compiler can remove the call all together.
2009-07-19 08:18:23 +02:00
Michal Nazarewicz
03e43356ce conf: replaced gcc "const" attribute with "pure"
The functions were not "const" (they examined values pointed
by arguments passed to them, quoting gcc's doc: "Note that
a function that has pointer arguments and examines the data
pointed to must _not_ be declared 'const'.") but rather
"pure" and still not all of them.

Note also, that even some of the functions declared "pure"
are not pure, however, due to reasons stated in source code
the attribute has been kept.
2009-07-19 08:18:08 +02:00
Michal Nazarewicz
d718a8b59d daemon: added "group" configuration option
The "group" configuration option is similar to "user" as it
sets user set what group MPD shall run as.  With "user"
option, MPD changed GID to the GID of the user, however,
more control could be desired.

Moreover, the patch changes the way of checking whether no
setuid(2)/setgid(2) is required -- previously user names
were compered, now UID and GIDs are compered (ie. the one we
already have (getuid(2)/getgid(2)) with the one we want to
change to).
2009-07-19 08:17:55 +02:00
Max Kellermann
4100035b19 Merged release 0.15.1 from branch 'v0.15.x' 2009-07-16 07:37:13 +02:00
Max Kellermann
9bef46c0da mapper: use g_file_test() instead of stat()
The GLib functions are more portable.
2009-07-15 18:58:24 +02:00
Max Kellermann
c134adbcbf mapper: pass music and playlist directory to mapper_init()
Added another glue function in main().
2009-07-15 18:58:19 +02:00
Max Kellermann
7bc8c7518b main: moved complex initialization code to glue functions
The glue_*() functions act as a glue between MPD's main() function and
its libraries.  They handle disabled features, and pass validated
configuration options.
2009-07-15 18:58:12 +02:00
Max Kellermann
739984f920 main: renamed openDB() to glue_db_init_and_load()
No CamelCase.
2009-07-15 18:58:10 +02:00
Max Kellermann
7261739526 cmdline: renamed option "--stdout" to "--stderr"
Since version 0.14, MPD has been logging to standard error instead of
standard output.  The option name should reflect that.  The old option
continues to work, we will remove it in a future MPD release.
2009-07-15 18:27:32 +02:00
Max Kellermann
62f9df98b4 cmdline: no CamelCase
Renamed type, variables and functions.
2009-07-15 18:22:49 +02:00
Max Kellermann
c76f71e8d6 conf: added the gcc "const" attribute to some functions
Add the "const" attribute to functions when their return value only
depends on parameters.  This allows gcc to eliminate some function
calls.
2009-07-15 17:12:48 +02:00
Max Kellermann
f7cc5b2efd state_file: don't rewind the stream while reading the state file
Parse the state file line by line, let each subsystem probe a line.
Only the playlist_state code gets the FILE pointer to read the
following lines.
2009-07-15 16:57:37 +02:00
Max Kellermann
df7d7732c6 state_file: simplified state_file_finish()
Return early from the destructor function when there is no configured
state file.  Don't check the timer, don't call g_free(NULL).
2009-07-15 14:32:29 +02:00
Max Kellermann
889b1c1eae state_file: fixed debug messages
Print "Loading" instead of "Saving" in state_file_read().  Added debug
message to state_file_write().
2009-07-15 14:29:30 +02:00
Serge Ziryukin
297d749fc8 modplug: get track length 2009-07-15 11:22:45 +02:00
Max Kellermann
6233de0546 encoder/twolame: new encoder plugin based on libtwolame
This encoder plugin is a replacement for the LAME encoder plugin for
those who prefer a "free" (non-patent encumbered) encoder library.
Most of the plugin source code is copied from the LAME encoder plugin,
since the LAME and TwoLAME APIs are nearly the same.
2009-07-14 23:07:41 +02:00
Max Kellermann
b1afa40fc1 configure.ac: use more MPD_AUTO_PKG()
Fail when a feature is enabled, but the library is not found.
2009-07-14 21:38:49 +02:00
Max Kellermann
a620e936cc Makefile.am: use WAVPACK_CFLAGS and WAVPACK_LIBS
Don't append these to MPD_CFLAGS and MPD_LIBS.
2009-07-14 21:29:01 +02:00
Max Kellermann
d897170455 playlist: no CamelCase 2009-07-14 21:28:36 +02:00
Max Kellermann
5a886da93b playlist: removed {save,read}PlaylistState()
Those were only wrappers for playlist_state_{save,restore}().  Since
sf_callbacks has been removed, we can call the latter functions
directly.
2009-07-14 21:28:26 +02:00
Max Kellermann
9206f54979 state_file: eliminated the sf_callbacks array
There are very few callbacks, and they are not meant to be pluggable.
Let's eliminate the array and call the load/save functions manually.
2009-07-14 21:15:12 +02:00
Serge Ziryukin
75c0a33ec5 flac: load external cue sheet when no internal one
External cue sheet file for "file.flac" should be named as "file.flac.cue".
2009-07-09 19:01:24 +02:00
Bart Nagel
8ae9b45da0 Implement ArtistSort tag 2009-07-09 19:00:41 +02:00
Max Kellermann
1eebbc746f decoder/sndfile: new decoder plugin based on libsndfile 2009-07-07 08:58:51 +02:00
Max Kellermann
adb2f66ced tag_id3: revised "performer" tag support
According to the ID3 2.4 documentation, "TOPE" is "Original
artist/performer", not "performer".  Removed "TOPE" support.  Instead,
map TPE3 ("Conductor/performer refinement") and TPE4 ("Interpreted,
remixed, or otherwise modified by") to "performer".
2009-07-07 08:00:21 +02:00
Max Kellermann
7246d67263 tag: added tag "AlbumArtistSort"
The tag_id3.c library supports both the documented "TSO2" tag, and the
inofficial TXXX/ALBUMARTISTSORT.

The Vorbis/FLAC decoder automatically supports the new tag, without
further change.
2009-07-07 07:36:25 +02:00
Max Kellermann
971fbe5d8e Makefile.am: disable test/run_encoder without plugins
If the encoder plugin API is disabled at compile time, don't compile
test/run_encoder.c.
2009-07-06 22:50:07 +02:00
Max Kellermann
45df3e5e54 main: fix "unused local variable" warning
The variables "success" and "error" are only used if SQLite support is
enabled.
2009-07-06 22:48:34 +02:00
Sébastien Houzé
468b7d3aea player_thread: log played song in "default" log mode
Very few lines to log a song URI when it has been entirely played.
Then mpd logs can be parsed to do statistics.
2009-07-06 22:29:58 +02:00
Max Kellermann
0275690b5c output: use the software mixer plugin
Do all the software volume stuff inside each output thread, not in the
player thread.  This allows one software mixer per output device, and
also allows the user to configure the mixer type (hardware or
software) for each audio output.

This moves the global "mixer_type" setting into the "audio_output"
section, deprecating the "mixer_enabled" flag.
2009-07-06 22:00:50 +02:00
Max Kellermann
da8095db54 mixer_all: added mixer_all_set_software_volume()
The special-purpose function is used for saving/restore the software
volume control to the state file.
2009-07-06 21:52:29 +02:00
Max Kellermann
5d74b1efef mixer/software: new mixer which controls filter/volume
This mixer plugin may be used instead of the traditional global
software mixer.  It integrates with the "volume" filter plugin, and
can control the software volume of an audio output which has no
hardware mixer.
2009-07-06 21:52:15 +02:00
Max Kellermann
d3b5574d7a volume: moved range check to handle_setvol()
Converted the range checks in volume_level_change() to assertions.
Changed all volume types to "unsigned", expect for those which must be
able to indicate error (-1).
2009-07-06 21:52:10 +02:00
Max Kellermann
90472526e0 volume, mixer: removed the "relative" parameter
Since the "volume" command has been removed, nobody uses relative
volumes anymore.
2009-07-06 21:51:24 +02:00
Max Kellermann
206392ad1a command: removed the "volume" command
This command has been deprecated more than 5 years ago (0.10.0).  Its
implementation is a kludge, let's remove it now.
2009-07-06 21:51:00 +02:00
Max Kellermann
171a9ee291 output_init: moved the "convert" filter at the end
The "convert" filter must be the last filter in the chain.  Ensure
that by doing its initialization at the very end of
audio_output_init().
2009-07-06 21:50:25 +02:00
Max Kellermann
13e725ab09 output_init: merge two local string variables 2009-07-06 21:50:13 +02:00
Max Kellermann
f5c2acf1d4 output_init: parse audio format earlier 2009-07-06 21:40:43 +02:00
Max Kellermann
17e3054399 song_print: check gmtime_r()'s return value
When song->mtime was not initialized properly, it was revealed that
strftime() might crash when gmtime_r() returns NULL due to an invalid
time_t input value.
2009-07-06 11:48:21 +02:00
Max Kellermann
09aadffe9b song: initialize mtime in song_alloc() 2009-07-06 11:32:31 +02:00
Max Kellermann
e47bdfe8e6 output: attach a filter chain to each audio_output
This patch adds initial filter support for audio outputs.  Each audio
output gets a "filter" attribute, which is used by ao_play_chunk().

The PCM conversion is now performed by convert_filter_plugin.
audio_output.convert_state has been removed.
2009-07-06 10:01:47 +02:00
Max Kellermann
cd9c0a6b3e filter/convert: new filter which calls pcm_convert() on demand 2009-07-06 10:01:02 +02:00
Max Kellermann
c372c3756b filter/chain: new library for creating a chain of filters 2009-07-06 10:01:02 +02:00
Max Kellermann
78fa3f06f9 output: added command REOPEN
REOPEN is called when the input audio format changes.  The output
thread may be reconfigure the PCM converter.
2009-07-06 10:01:02 +02:00
Max Kellermann
1350cd0e42 output_thread: moved OPEN handler to ao_open() 2009-07-06 08:04:18 +02:00
Max Kellermann
89d4f438c0 mixer_type: moved volume_mixer_type from volume.c 2009-07-06 07:37:37 +02:00
Max Kellermann
8bd7b5b607 conf: use bp->value, not param->value
A recent change to the boolean parser introduced a bug: instead of
using the block_param's value with get_bool(), we passed param->value
(which is always NULL in this case).
2009-07-06 07:37:32 +02:00
Max Kellermann
d78be1ab49 output_init: moved mixer instantiation to separate function 2009-07-05 18:44:37 +02:00
Max Kellermann
86e279f886 filter/volume: don't use volume_level_get()
Added public methods to get and set the current volume.
2009-07-05 16:31:47 +02:00
Max Kellermann
d4914fc9ef idle: added "update" event
Some clients have visual feedback for "database update is running".
Using the "database" idle event is unreliable, because it is only
emitted when the database was actually modified.  This patch adds the
"update" event, which is emitted when the update is started, and again
when the update is finished, disregarding whether it has been
modified.
2009-07-05 08:46:53 +02:00
Max Kellermann
46c19b8249 song_print: send song modification time to client
Added the response line "Last-Modified", which sends the modification
time in ISO 8601.  The same was already implemented for playlists.
2009-07-05 08:40:29 +02:00
Max Kellermann
077b24d62d configure.ac: increased protocol version to 0.16.0
We're going to extend the protocol now.  Update the protocol version.
2009-07-05 08:40:26 +02:00
Max Kellermann
111c4dac63 song_save: use GError for error handling 2009-07-05 08:29:52 +02:00
Max Kellermann
69ff9d757f song_save: no CamelCase
Renamed functions.
2009-07-05 08:29:50 +02:00
Max Kellermann
0c2ab17e91 sticker: use GError for error handling 2009-07-05 08:29:47 +02:00
Max Kellermann
12e82b9e33 test: added program "run_filter"
This program runs filter plugins in an isolated environment.
2009-07-05 06:54:48 +02:00
Max Kellermann
644fc48776 conf: registered option "filter"
Add this option to the user's manual.
2009-07-05 06:54:45 +02:00
Max Kellermann
666b1fae79 volume: removed unused macros
Removed VOLUME_MIXER_OSS and VOLUME_MIXER_ALSA.
2009-07-03 18:51:01 +02:00
Max Kellermann
e3c436f411 filter: added "volume" plugin
The "volume" filter plugin will replace the current software volume
code.  One "volume" filter may be attached to each output device.
This will allow the user to use hardware mixers for some devices, and
software mixers for other devices at the same time.

Currently, neither the filter API nor the "volume" plugin is
integrated into MPD.
2009-07-03 01:06:17 +02:00
Max Kellermann
48f3e13bec filter: added new plugin API for filters
The filter API allows us to implement software volume as a pluggable
filter, and we will be able to integrate libraries like SoX.
2009-07-03 01:02:53 +02:00
Max Kellermann
a923080d9b conf: added config_quark()
This GQuark will be used for GErrors related to malformed
configuration.
2009-07-03 01:01:50 +02:00
Max Kellermann
706614b0d7 playlist_state: don't save "current" song when none is set
This patch fixes an assertion failure:

 Assertion `order < queue->length' failed.

This happens when the state file is saved, when there is no "current"
song: current==-1, and queue_order_to_position(-1) is called.
2009-06-29 22:27:57 +02:00
Christopher Zimmerman
a641f562f3 playlist_state: save state when stopped
At the moment mpd doesn't store or restore the current track to/from
its state file when the daemon is stopped/started while in 'stopped'
state.  I believe the preferred behaviour would be to store and
restore the current track even when the daemon is in stopped state
when shutting down.

I made a small patch to adapt this behaviour. If you believe this is
not the preferred behaviour, maybe this should be realized as a
configuration option. I'm not sure how to do this, but made a small
comment, where one would have to put the option.
2009-06-26 09:26:12 +02:00
Max Kellermann
fe96bdf7e6 autogen.sh: don't check for autoconf <2.60
configure.ac refuses to run with autoconf older than 2.60, don't
bother to test for those versions in autogen.sh.
2009-06-25 11:53:34 +02:00
Max Kellermann
5b78b46a30 Makefile.am: require automake 1.10
Refuse to build with automake 1.9.  1.9 is quite old already, and I'm
too lazy to test with ancient versions.
2009-06-25 11:52:55 +02:00
Max Kellermann
eacd604518 ffmpeg: support multiple tags
Call av_metadata_get() in a loop.
2009-06-25 08:43:59 +02:00
Max Kellermann
637c6a1850 conf: log unused/unknown block parameters 2009-06-25 08:42:25 +02:00
Max Kellermann
0cbc4012e8 conf: added "used" flag to struct block_param 2009-06-25 08:41:23 +02:00
Max Kellermann
8074b82653 conf: detect duplicate parameters in config_add_block_param()
Moved the check from config_get_block_param().  Detect the duplicate
parameter when it's added, not when it's queried.
2009-06-25 08:41:09 +02:00
Max Kellermann
5d583c9b2d conf: make get_bool() return a bool
Instead of returning an artificial three-state integer, return a
"success" value and put the boolean value into a "bool" pointer.
That's a little bit more overhead, but an API which looks more
natural.
2009-06-25 08:40:02 +02:00
Max Kellermann
093e900d44 conf: moved code from get_bool() to string_array_contains() 2009-06-25 08:39:53 +02:00
Max Kellermann
8965b66ce4 conf: register configuration options statically
Initialize the config_entries array at compile time.  This is not only
faster, but also smaller.
2009-06-25 08:39:44 +02:00
Max Kellermann
146add67c2 conf: removed config_add_param()
This function is unused.
2009-06-25 08:39:01 +02:00
Max Kellermann
34e9a0a960 conf: replace "mask" bit field with two "bool" variables
Due to padding, this takes the same amount of memory.
2009-06-25 08:38:51 +02:00
Max Kellermann
b1e95b1fa8 volume: removed support for legacy mixer configuration
The top-level "mixer_device" and "mixer_control" options have been
deprecated by MPD 0.15, and it's safe to remove them in MPD 0.16.
2009-06-25 08:37:51 +02:00
Max Kellermann
ce072b89d2 command: added new "status" line with more precise "elapsed time" 2009-06-25 08:37:34 +02:00
Daniel Seuthe
4ffd9bce5a Preamp for missing replay-gain 2009-06-25 08:36:35 +02:00
Avuton Olrich
f16d05c633 Modify version string to post-release version 0.16~git 2009-06-24 17:34:27 -07:00
473 changed files with 35291 additions and 13717 deletions

9
.gitignore vendored
View File

@@ -24,6 +24,7 @@ config.sub
config_detected.h
config_detected.mk
configure
configure.lineno
depcomp
depmode
install-sh
@@ -32,9 +33,11 @@ ltmain.sh
missing
mkinstalldirs
mpd
mpd.exe
stamp-h1
tags
*~
.#*
.stgit*
doc/protocol.html
doc/protocol
@@ -43,10 +46,16 @@ doc/developer
doc/sticker
doc/api
test/software_volume
test/run_convert
test/run_decoder
test/read_tags
test/run_filter
test/run_encoder
test/run_output
test/read_conf
test/run_input
test/read_mixer
test/dump_playlist
test/run_normalize
test/tmp
test/run_inotify

View File

@@ -19,9 +19,6 @@ Eric Wollesen <encoded@xmtp.net>
Thomas Jansen <mithi@mithi.net>
multithreading tweaks, miscellaneous
Rasmus Steinke <rasi1979@googlemail.com>
documentation
Romain Bignon <romain@peerfuse.org>
playlist manipulation

17
INSTALL
View File

@@ -13,7 +13,7 @@ Dependencies
gcc - http://gcc.gnu.org/
Any other C99 compliant compiler should also work.
glib - http://www.gtk.org/
GLib 2.12 - http://www.gtk.org/
General-purpose utility library.
@@ -56,6 +56,12 @@ libshout - http://www.icecast.org/
For streaming to an Icecast or Shoutcast server.
You also need an encoder: either libvorbisenc (ogg), or liblame (mp3).
OpenAL - http://kcat.strangesoft.net/openal.html
Open Audio Library
libffado - http://www.ffado.org/
For FireWire audio devices.
Optional Input Dependencies
---------------------------
@@ -69,6 +75,9 @@ MAD - http://www.underbit.com/products/mad/
For MP3 support. You will need libmad, and optionally libid3tag if you want
ID3 tag support.
libmpg123 - http://www.mpg123.de/
Alternative for MP3 support.
Ogg Vorbis - http://www.xiph.org/ogg/vorbis/
For Ogg Vorbis support. You will need libogg and libvorbis.
@@ -104,6 +113,12 @@ For MIDI support (DO NOT USE - use libwildmidi instead)
libwildmidi - http://wildmidi.sourceforge.net/
For MIDI support.
libsndfile - http://www.mega-nerd.com/libsndfile/
WAVE, AIFF, and many others.
libwavpack - http://www.wavpack.com/
For WavPack playback.
Optional Miscellaneous Dependencies
-----------------------------------

View File

@@ -1,12 +1,15 @@
ACLOCAL_AMFLAGS = -I m4
AUTOMAKE_OPTIONS = foreign 1.9 dist-bzip2
AUTOMAKE_OPTIONS = foreign 1.10 dist-bzip2 subdir-objects
AM_CPPFLAGS = -I$(srcdir)/src $(GLIB_CFLAGS)
AM_CPPFLAGS += -DSYSTEM_CONFIG_FILE_LOCATION='"$(sysconfdir)/mpd.conf"'
bin_PROGRAMS = src/mpd
src_mpd_CFLAGS = $(AM_CFLAGS) $(MPD_CFLAGS)
src_mpd_CPPFLAGS = $(AM_CPPFLAGS) \
$(LIBWRAP_CFLAGS) \
$(SQLITE_CFLAGS) \
$(ARCHIVE_CFLAGS) \
$(INPUT_CFLAGS) \
@@ -16,6 +19,7 @@ src_mpd_CPPFLAGS = $(AM_CPPFLAGS) \
$(FILTER_CFLAGS) \
$(OUTPUT_CFLAGS)
src_mpd_LDADD = $(MPD_LIBS) \
$(LIBWRAP_LDFLAGS) \
$(SQLITE_LIBS) \
$(ARCHIVE_LIBS) \
$(INPUT_LIBS) \
@@ -27,10 +31,12 @@ src_mpd_LDADD = $(MPD_LIBS) \
$(GLIB_LIBS)
mpd_headers = \
src/check.h \
src/notify.h \
src/ack.h \
src/audio.h \
src/audio_format.h \
src/audio_check.h \
src/audio_parser.h \
src/output_internal.h \
src/output_api.h \
@@ -42,7 +48,15 @@ mpd_headers = \
src/output_state.h \
src/output_print.h \
src/output_command.h \
src/buffer2array.h \
src/filter_internal.h \
src/filter_config.h \
src/filter_plugin.h \
src/filter_registry.h \
src/filter/autoconvert_filter_plugin.h \
src/filter/chain_filter_plugin.h \
src/filter/convert_filter_plugin.h \
src/filter/replay_gain_filter_plugin.h \
src/filter/volume_filter_plugin.h \
src/command.h \
src/idle.h \
src/cmdline.h \
@@ -64,23 +78,40 @@ mpd_headers = \
src/encoder_plugin.h \
src/encoder_list.h \
src/encoder_api.h \
src/exclude.h \
src/fd_util.h \
src/fifo_buffer.h \
src/glib_compat.h \
src/update.h \
src/update_internal.h \
src/inotify_source.h \
src/inotify_queue.h \
src/inotify_update.h \
src/dirvec.h \
src/gcc.h \
src/decoder_list.h \
src/decoder_print.h \
src/decoder/flac_compat.h \
src/decoder/flac_metadata.h \
src/decoder/flac_pcm.h \
src/decoder/_flac_common.h \
src/decoder/_ogg_common.h \
src/input_init.h \
src/input_plugin.h \
src/input_registry.h \
src/input_stream.h \
src/input/file_input_plugin.h \
src/input/ffmpeg_input_plugin.h \
src/input/curl_input_plugin.h \
src/input/rewind_input_plugin.h \
src/input/lastfm_input_plugin.h \
src/input/mms_input_plugin.h \
src/text_file.h \
src/text_input_stream.h \
src/icy_server.h \
src/icy_metadata.h \
src/client.h \
src/client_internal.h \
src/server_socket.h \
src/listen.h \
src/log.h \
src/ls.h \
@@ -91,27 +122,35 @@ mpd_headers = \
src/mixer_list.h \
src/event_pipe.h \
src/mixer_plugin.h \
src/mixer_type.h \
src/mixer/software_mixer_plugin.h \
src/mixer/pulse_mixer_plugin.h \
src/daemon.h \
src/normalize.h \
src/compress.h \
src/AudioCompress/config.h \
src/AudioCompress/compress.h \
src/buffer.h \
src/pipe.h \
src/chunk.h \
src/path.h \
src/mapper.h \
src/open.h \
src/output/httpd_client.h \
src/output/httpd_internal.h \
src/output/pulse_output_plugin.h \
src/output/winmm_output_plugin.h \
src/page.h \
src/pcm_buffer.h \
src/pcm_utils.h \
src/pcm_convert.h \
src/pcm_volume.h \
src/pcm_mix.h \
src/pcm_byteswap.h \
src/pcm_channels.h \
src/pcm_format.h \
src/pcm_resample.h \
src/pcm_resample_internal.h \
src/pcm_dither.h \
src/pcm_pack.h \
src/pcm_prng.h \
src/permission.h \
src/player_thread.h \
@@ -121,13 +160,32 @@ mpd_headers = \
src/playlist_print.h \
src/playlist_save.h \
src/playlist_state.h \
src/playlist_plugin.h \
src/playlist_list.h \
src/playlist_mapper.h \
src/playlist_any.h \
src/playlist_song.h \
src/playlist_queue.h \
src/playlist_vector.h \
src/playlist_database.h \
src/playlist/extm3u_playlist_plugin.h \
src/playlist/m3u_playlist_plugin.h \
src/playlist/pls_playlist_plugin.h \
src/playlist/xspf_playlist_plugin.h \
src/playlist/asx_playlist_plugin.h \
src/playlist/rss_playlist_plugin.h \
src/playlist/lastfm_playlist_plugin.h \
src/playlist/cue_playlist_plugin.h \
src/playlist/flac_playlist_plugin.h \
src/poison.h \
src/riff.h \
src/aiff.h \
src/queue.h \
src/queue_print.h \
src/queue_save.h \
src/replay_gain.h \
src/refcount.h \
src/replay_gain_config.h \
src/replay_gain_info.h \
src/sig_handlers.h \
src/song.h \
src/song_print.h \
@@ -142,10 +200,13 @@ mpd_headers = \
src/tag.h \
src/tag_internal.h \
src/tag_pool.h \
src/tag_table.h \
src/tag_ape.h \
src/tag_id3.h \
src/tag_rva2.h \
src/tag_print.h \
src/tag_save.h \
src/tokenizer.h \
src/strset.h \
src/uri.h \
src/utils.h \
@@ -157,22 +218,30 @@ mpd_headers = \
src/archive_api.h \
src/archive_internal.h \
src/archive_list.h \
src/archive_plugin.h \
src/archive/bz2_archive_plugin.h \
src/archive/iso9660_archive_plugin.h \
src/archive/zzip_archive_plugin.h \
src/input/archive_input_plugin.h \
src/cue/cue_tag.h
src/cue/cue_tag.h\
src/mpd_error.h
src_mpd_SOURCES = \
$(mpd_headers) \
$(ARCHIVE_SRC) \
$(INPUT_SRC) \
$(PLAYLIST_SRC) \
$(TAG_SRC) \
$(DECODER_SRC) \
$(ENCODER_SRC) \
$(OUTPUT_API_SRC) $(OUTPUT_SRC) \
$(MIXER_API_SRC) $(MIXER_SRC) \
$(FILTER_SRC) \
src/notify.c \
src/audio.c \
src/audio_check.c \
src/audio_format.c \
src/audio_parser.c \
src/buffer2array.c \
src/command.c \
src/idle.c \
src/cmdline.c \
@@ -183,22 +252,41 @@ src_mpd_SOURCES = \
src/decoder_control.c \
src/decoder_api.c \
src/decoder_internal.c \
src/decoder_print.c \
src/directory.c \
src/directory_save.c \
src/directory_print.c \
src/database.c \
src/dirvec.c \
src/exclude.c \
src/fd_util.c \
src/fifo_buffer.c \
src/filter_config.c \
src/filter_plugin.c \
src/filter_registry.c \
src/update.c \
src/update_queue.c \
src/update_walk.c \
src/update_remove.c \
src/client.c \
src/client_event.c \
src/client_expire.c \
src/client_global.c \
src/client_idle.c \
src/client_list.c \
src/client_new.c \
src/client_process.c \
src/client_read.c \
src/client_write.c \
src/server_socket.c \
src/listen.c \
src/log.c \
src/ls.c \
src/main.c \
src/main_win32.c \
src/event_pipe.c \
src/daemon.c \
src/normalize.c \
src/compress.c \
src/AudioCompress/compress.c \
src/buffer.c \
src/pipe.c \
src/chunk.c \
@@ -208,7 +296,9 @@ src_mpd_SOURCES = \
src/pcm_convert.c \
src/pcm_volume.c \
src/pcm_mix.c \
src/pcm_byteswap.c \
src/pcm_channels.c \
src/pcm_pack.c \
src/pcm_format.c \
src/pcm_resample.c \
src/pcm_resample_fallback.c \
@@ -222,13 +312,21 @@ src_mpd_SOURCES = \
src/playlist_edit.c \
src/playlist_print.c \
src/playlist_save.c \
src/playlist_mapper.c \
src/playlist_any.c \
src/playlist_song.c \
src/playlist_state.c \
src/playlist_queue.c \
src/playlist_vector.c \
src/playlist_database.c \
src/queue.c \
src/queue_print.c \
src/queue_save.c \
src/replay_gain.c \
src/replay_gain_config.c \
src/replay_gain_info.c \
src/sig_handlers.c \
src/song.c \
src/song_update.c \
src/song_print.c \
src/song_save.c \
src/songvec.c \
@@ -239,6 +337,9 @@ src_mpd_SOURCES = \
src/tag_pool.c \
src/tag_print.c \
src/tag_save.c \
src/tokenizer.c \
src/text_file.c \
src/text_input_stream.c \
src/strset.c \
src/uri.c \
src/utils.c \
@@ -247,6 +348,13 @@ src_mpd_SOURCES = \
src/stored_playlist.c \
src/timer.c
if ENABLE_INOTIFY
src_mpd_SOURCES += \
src/inotify_source.c \
src/inotify_queue.c \
src/inotify_update.c
endif
if ENABLE_SQLITE
src_mpd_SOURCES += \
src/sticker.c \
@@ -276,21 +384,22 @@ ARCHIVE_LIBS = \
ARCHIVE_SRC =
if HAVE_BZ2
ARCHIVE_SRC += src/archive/bz2_plugin.c
ARCHIVE_SRC += src/archive/bz2_archive_plugin.c
endif
if HAVE_ZIP
ARCHIVE_SRC += src/archive/zip_plugin.c
if HAVE_ZZIP
ARCHIVE_SRC += src/archive/zzip_archive_plugin.c
endif
if HAVE_ISO
ARCHIVE_SRC += src/archive/iso_plugin.c
if HAVE_ISO9660
ARCHIVE_SRC += src/archive/iso9660_archive_plugin.c
endif
if ENABLE_ARCHIVE
ARCHIVE_SRC += \
src/archive_api.c \
src/archive_list.c \
src/archive_plugin.c \
src/input/archive_input_plugin.c
endif
@@ -307,6 +416,7 @@ TAG_SRC = \
if HAVE_ID3TAG
TAG_SRC += src/tag_id3.c \
src/tag_rva2.c \
src/riff.c src/aiff.c
endif
@@ -315,51 +425,64 @@ endif
DECODER_CFLAGS = \
$(VORBIS_CFLAGS) $(TREMOR_CFLAGS) \
$(patsubst -I%/FLAC,-I%,$(FLAC_CFLAGS)) \
$(SNDFILE_CFLAGS) \
$(AUDIOFILE_CFLAGS) \
$(LIBMIKMOD_CFLAGS) \
$(MODPLUG_CFLAGS) \
$(GME_CFLAGS) \
$(SIDPLAY_CFLAGS) \
$(FLUIDSYNTH_CFLAGS) \
$(WILDMIDI_CFLAGS) \
$(WAVPACK_CFLAGS) \
$(MAD_CFLAGS) \
$(MPG123_CFLAGS) \
$(FFMPEG_CFLAGS) \
$(CUE_CFLAGS)
DECODER_LIBS = \
$(VORBIS_LIBS) $(TREMOR_LIBS) \
$(FLAC_LIBS) \
$(SNDFILE_LIBS) \
$(AUDIOFILE_LIBS) $(LIBMIKMOD_LIBS) \
$(MODPLUG_LIBS) \
$(GME_LIBS) \
$(SIDPLAY_LIBS) \
$(FLUIDSYNTH_LIBS) \
$(WILDMIDI_LIBS) \
$(WAVPACK_LIBS) \
$(MAD_LIBS) \
$(MPG123_LIBS) \
$(MP4FF_LIBS) \
$(FFMPEG_LIBS) \
$(CUE_LIBS)
DECODER_SRC = \
src/decoder_buffer.c \
src/decoder_plugin.c \
src/decoder_list.c
if HAVE_MAD
DECODER_SRC += src/decoder/mad_plugin.c
DECODER_SRC += src/decoder/mad_decoder_plugin.c
endif
if HAVE_MPG123
DECODER_SRC += src/decoder/mpg123_decoder_plugin.c
endif
if HAVE_MPCDEC
DECODER_SRC += src/decoder/mpcdec_plugin.c
DECODER_SRC += src/decoder/mpcdec_decoder_plugin.c
endif
if HAVE_WAVPACK
DECODER_SRC += src/decoder/wavpack_plugin.c
DECODER_SRC += src/decoder/wavpack_decoder_plugin.c
endif
if HAVE_FAAD
DECODER_SRC += src/decoder/faad_plugin.c
DECODER_SRC += src/decoder/faad_decoder_plugin.c
endif
if HAVE_MP4
DECODER_SRC += src/decoder/mp4ff_plugin.c
DECODER_SRC += src/decoder/mp4ff_decoder_plugin.c
endif
if HAVE_OGG_COMMON
@@ -367,63 +490,83 @@ DECODER_SRC += src/decoder/_ogg_common.c
endif
if HAVE_FLAC_COMMON
DECODER_SRC += src/decoder/_flac_common.c
DECODER_SRC += \
src/decoder/flac_metadata.c \
src/decoder/flac_pcm.c \
src/decoder/_flac_common.c
endif
if ENABLE_VORBIS_DECODER
DECODER_SRC += src/decoder/vorbis_plugin.c
DECODER_SRC += src/decoder/vorbis_decoder_plugin.c
endif
if HAVE_FLAC
DECODER_SRC += src/decoder/flac_plugin.c
DECODER_SRC += src/decoder/flac_decoder_plugin.c
endif
if HAVE_OGGFLAC
DECODER_SRC += src/decoder/oggflac_plugin.c
DECODER_SRC += src/decoder/oggflac_decoder_plugin.c
endif
if HAVE_AUDIOFILE
DECODER_SRC += src/decoder/audiofile_plugin.c
DECODER_SRC += src/decoder/audiofile_decoder_plugin.c
endif
if ENABLE_MIKMOD_DECODER
DECODER_SRC += src/decoder/mikmod_plugin.c
DECODER_SRC += src/decoder/mikmod_decoder_plugin.c
endif
if HAVE_MODPLUG
DECODER_SRC += src/decoder/modplug_plugin.c
DECODER_SRC += src/decoder/modplug_decoder_plugin.c
endif
if ENABLE_SIDPLAY
DECODER_SRC += src/decoder/sidplay_plugin.cxx
DECODER_SRC += src/decoder/sidplay_decoder_plugin.cxx
endif
if ENABLE_FLUIDSYNTH
DECODER_SRC += src/decoder/fluidsynth_plugin.c
DECODER_SRC += src/decoder/fluidsynth_decoder_plugin.c
endif
if ENABLE_WILDMIDI
DECODER_SRC += src/decoder/wildmidi_plugin.c
DECODER_SRC += src/decoder/wildmidi_decoder_plugin.c
endif
if HAVE_FFMPEG
DECODER_SRC += src/decoder/ffmpeg_plugin.c
DECODER_SRC += src/decoder/ffmpeg_decoder_plugin.c
endif
if ENABLE_SNDFILE
DECODER_SRC += src/decoder/sndfile_decoder_plugin.c
endif
if HAVE_GME
DECODER_SRC += src/decoder/gme_decoder_plugin.c
endif
# encoder plugins
ENCODER_CFLAGS = \
$(LAME_CFLAGS) \
$(TWOLAME_CFLAGS) \
$(patsubst -I%/FLAC,-I%,$(FLAC_CFLAGS)) \
$(VORBISENC_CFLAGS)
ENCODER_LIBS = \
$(LAME_LIBS) \
$(TWOLAME_LIBS) \
$(FLAC_LIBS) \
$(VORBISENC_LIBS)
ENCODER_SRC =
if ENABLE_ENCODER
ENCODER_SRC += src/encoder_list.c
ENCODER_SRC += src/encoder/null_encoder.c
if ENABLE_WAVE_ENCODER
ENCODER_SRC += src/encoder/wave_encoder.c
endif
if ENABLE_VORBIS_ENCODER
ENCODER_SRC += src/encoder/vorbis_encoder.c
@@ -432,6 +575,14 @@ endif
if ENABLE_LAME_ENCODER
ENCODER_SRC += src/encoder/lame_encoder.c
endif
if ENABLE_TWOLAME_ENCODER
ENCODER_SRC += src/encoder/twolame_encoder.c
endif
if ENABLE_FLAC_ENCODER
ENCODER_SRC += src/encoder/flac_encoder.c
endif
endif
@@ -457,24 +608,28 @@ endif
INPUT_CFLAGS = \
$(CURL_CFLAGS) \
$(FFMPEG_CFLAGS) \
$(MMS_CFLAGS)
INPUT_LIBS = \
$(CURL_LIBS) \
$(FFMPEG_LIBS) \
$(MMS_LIBS)
INPUT_SRC = \
src/input_init.c \
src/input_registry.c \
src/input_stream.c \
src/input/rewind_input_plugin.c \
src/input/file_input_plugin.c
if HAVE_CURL
if ENABLE_CURL
INPUT_SRC += src/input/curl_input_plugin.c \
src/input/rewind_input_plugin.c \
src/icy_metadata.c
endif
if ENABLE_LASTFM
INPUT_SRC += src/input/lastfm_input_plugin.c
if HAVE_FFMPEG
INPUT_SRC += src/input/ffmpeg_input_plugin.c
endif
if ENABLE_MMS
@@ -485,14 +640,19 @@ endif
OUTPUT_CFLAGS = \
$(AO_CFLAGS) \
$(ALSA_CFLAGS) \
$(FFADO_CFLAGS) \
$(JACK_CFLAGS) \
$(OPENAL_CFLAGS) \
$(PULSE_CFLAGS) \
$(SHOUT_CFLAGS)
OUTPUT_LIBS = \
$(LIBWRAP_LDFLAGS) \
$(AO_LIBS) \
$(ALSA_LIBS) \
$(FFADO_LIBS) \
$(JACK_LIBS) \
$(OPENAL_LIBS) \
$(PULSE_LIBS) \
$(SHOUT_LIBS)
@@ -511,14 +671,20 @@ OUTPUT_SRC = \
MIXER_API_SRC = \
src/mixer_control.c \
src/mixer_type.c \
src/mixer_all.c \
src/mixer_api.c
MIXER_SRC =
MIXER_SRC = \
src/mixer/software_mixer_plugin.c
if HAVE_ALSA
OUTPUT_SRC += src/output/alsa_plugin.c
MIXER_SRC += src/mixer/alsa_mixer.c
MIXER_SRC += src/mixer/alsa_mixer_plugin.c
endif
if ENABLE_FFADO_OUTPUT
OUTPUT_SRC += src/output/ffado_output_plugin.c
endif
if HAVE_AO
@@ -526,7 +692,7 @@ OUTPUT_SRC += src/output/ao_plugin.c
endif
if HAVE_FIFO
OUTPUT_SRC += src/output/fifo_plugin.c
OUTPUT_SRC += src/output/fifo_output_plugin.c
endif
if ENABLE_PIPE_OUTPUT
@@ -534,7 +700,7 @@ OUTPUT_SRC += src/output/pipe_output_plugin.c
endif
if HAVE_JACK
OUTPUT_SRC += src/output/jack_plugin.c
OUTPUT_SRC += src/output/jack_output_plugin.c
endif
if HAVE_MVP
@@ -543,7 +709,11 @@ endif
if HAVE_OSS
OUTPUT_SRC += src/output/oss_plugin.c
MIXER_SRC += src/mixer/oss_mixer.c
MIXER_SRC += src/mixer/oss_mixer_plugin.c
endif
if HAVE_OPENAL
OUTPUT_SRC += src/output/openal_plugin.c
endif
if HAVE_OSX
@@ -551,14 +721,18 @@ OUTPUT_SRC += src/output/osx_plugin.c
endif
if HAVE_PULSE
OUTPUT_SRC += src/output/pulse_plugin.c
MIXER_SRC += src/mixer/pulse_mixer.c
OUTPUT_SRC += src/output/pulse_output_plugin.c
MIXER_SRC += src/mixer/pulse_mixer_plugin.c
endif
if HAVE_SHOUT
OUTPUT_SRC += src/output/shout_plugin.c
endif
if ENABLE_RECORDER_OUTPUT
OUTPUT_SRC += src/output/recorder_output_plugin.c
endif
if ENABLE_HTTPD_OUTPUT
OUTPUT_SRC += \
src/icy_server.c \
@@ -570,6 +744,52 @@ if ENABLE_SOLARIS_OUTPUT
OUTPUT_SRC += src/output/solaris_output_plugin.c
endif
if ENABLE_WINMM_OUTPUT
OUTPUT_SRC += src/output/winmm_output_plugin.c
MIXER_SRC += src/mixer/winmm_mixer_plugin.c
endif
#
# Playlist plugins
#
PLAYLIST_SRC = \
src/playlist/extm3u_playlist_plugin.c \
src/playlist/m3u_playlist_plugin.c \
src/playlist/pls_playlist_plugin.c \
src/playlist/xspf_playlist_plugin.c \
src/playlist/asx_playlist_plugin.c \
src/playlist/rss_playlist_plugin.c \
src/playlist_list.c
if ENABLE_LASTFM
PLAYLIST_SRC += src/playlist/lastfm_playlist_plugin.c
endif
if HAVE_CUE
PLAYLIST_SRC += src/playlist/cue_playlist_plugin.c
endif
if HAVE_FLAC
PLAYLIST_SRC += src/playlist/flac_playlist_plugin.c
endif
#
# Filter plugins
#
FILTER_SRC = \
src/filter/null_filter_plugin.c \
src/filter/chain_filter_plugin.c \
src/filter/autoconvert_filter_plugin.c \
src/filter/convert_filter_plugin.c \
src/filter/route_filter_plugin.c \
src/filter/normalize_filter_plugin.c \
src/filter/replay_gain_filter_plugin.c \
src/filter/volume_filter_plugin.c
#
# Sparse code analysis
@@ -597,21 +817,31 @@ sparse-check:
if ENABLE_TEST
TESTS =
noinst_PROGRAMS = \
test/read_conf \
test/run_input \
test/dump_playlist \
test/run_decoder \
test/read_tags \
test/run_filter \
test/run_output \
test/read_mixer \
test/run_convert \
test/run_normalize \
test/software_volume
if HAVE_ALSA
# this debug program is still ALSA specific
noinst_PROGRAMS += test/read_mixer
endif
test_read_conf_CPPFLAGS = $(AM_CPPFLAGS) \
$(GLIB_CFLAGS)
test_read_conf_LDADD = $(MPD_LIBS) \
$(GLIB_LIBS)
test_read_conf_SOURCES = test/read_conf.c \
src/conf.c src/buffer2array.c src/utils.c
src/conf.c src/tokenizer.c src/utils.c
test_run_input_CPPFLAGS = $(AM_CPPFLAGS) \
$(ARCHIVE_CFLAGS) \
@@ -621,11 +851,43 @@ test_run_input_LDADD = $(MPD_LIBS) \
$(INPUT_LIBS) \
$(GLIB_LIBS)
test_run_input_SOURCES = test/run_input.c \
src/conf.c src/buffer2array.c src/utils.c \
src/conf.c src/tokenizer.c src/utils.c \
src/tag.c src/tag_pool.c src/tag_save.c \
src/fd_util.c \
$(ARCHIVE_SRC) \
$(INPUT_SRC)
test_dump_playlist_CPPFLAGS = $(AM_CPPFLAGS) \
$(CUE_CFLAGS) \
$(patsubst -I%/FLAC,-I%,$(FLAC_CFLAGS)) \
$(ARCHIVE_CFLAGS) \
$(INPUT_CFLAGS)
test_dump_playlist_LDADD = $(MPD_LIBS) \
$(CUE_LIBS) \
$(FLAC_LIBS) \
$(ARCHIVE_LIBS) \
$(INPUT_LIBS) \
$(GLIB_LIBS)
test_dump_playlist_SOURCES = test/dump_playlist.c \
src/conf.c src/tokenizer.c src/utils.c \
src/uri.c \
src/song.c src/tag.c src/tag_pool.c src/tag_save.c \
src/text_input_stream.c src/fifo_buffer.c \
src/fd_util.c \
$(ARCHIVE_SRC) \
$(INPUT_SRC) \
$(PLAYLIST_SRC)
if HAVE_CUE
test_dump_playlist_SOURCES += src/cue/cue_tag.c
endif
if HAVE_FLAC
test_dump_playlist_SOURCES += \
src/replay_gain_info.c \
src/decoder/flac_metadata.c
endif
test_run_decoder_CPPFLAGS = $(AM_CPPFLAGS) \
$(TAG_CFLAGS) \
$(ARCHIVE_CFLAGS) \
@@ -636,10 +898,13 @@ test_run_decoder_LDADD = $(MPD_LIBS) \
$(INPUT_LIBS) $(DECODER_LIBS) \
$(GLIB_LIBS)
test_run_decoder_SOURCES = test/run_decoder.c \
src/conf.c src/buffer2array.c src/utils.c src/log.c \
src/conf.c src/tokenizer.c src/utils.c src/log.c \
src/tag.c src/tag_pool.c \
src/replay_gain.c \
src/replay_gain_info.c \
src/uri.c \
src/fd_util.c \
src/audio_check.c \
src/audio_format.c \
src/timer.c \
$(ARCHIVE_SRC) \
$(INPUT_SRC) \
@@ -656,22 +921,50 @@ test_read_tags_LDADD = $(MPD_LIBS) \
$(INPUT_LIBS) $(DECODER_LIBS) \
$(GLIB_LIBS)
test_read_tags_SOURCES = test/read_tags.c \
src/conf.c src/buffer2array.c src/utils.c src/log.c \
src/conf.c src/tokenizer.c src/utils.c src/log.c \
src/tag.c src/tag_pool.c \
src/replay_gain.c \
src/replay_gain_info.c \
src/uri.c \
src/fd_util.c \
src/audio_check.c \
src/timer.c \
$(ARCHIVE_SRC) \
$(INPUT_SRC) \
$(TAG_SRC) \
$(DECODER_SRC)
test_run_filter_CPPFLAGS = $(AM_CPPFLAGS)
test_run_filter_LDADD = $(MPD_LIBS) \
$(SAMPLERATE_LIBS) \
$(GLIB_LIBS)
test_run_filter_SOURCES = test/run_filter.c \
src/filter_plugin.c \
src/filter_registry.c \
src/conf.c src/tokenizer.c src/utils.c \
src/pcm_volume.c src/pcm_convert.c src/pcm_byteswap.c \
src/pcm_format.c src/pcm_channels.c src/pcm_dither.c \
src/pcm_pack.c \
src/pcm_resample.c src/pcm_resample_fallback.c \
src/audio_check.c \
src/audio_format.c \
src/audio_parser.c \
src/replay_gain_config.c \
src/replay_gain_info.c \
src/AudioCompress/compress.c \
$(FILTER_SRC)
if HAVE_LIBSAMPLERATE
test_run_filter_SOURCES += src/pcm_resample_libsamplerate.c
endif
if ENABLE_ENCODER
noinst_PROGRAMS += test/run_encoder
test_run_encoder_SOURCES = test/run_encoder.c \
src/conf.c src/buffer2array.c \
src/conf.c src/tokenizer.c \
src/utils.c \
src/tag.c src/tag_pool.c \
src/audio_check.c \
src/audio_format.c \
src/audio_parser.c \
$(ENCODER_SRC)
test_run_encoder_LDADD = $(MPD_LIBS) \
@@ -680,11 +973,42 @@ test_run_encoder_LDADD = $(MPD_LIBS) \
endif
test_software_volume_SOURCES = test/software_volume.c \
src/audio_check.c \
src/audio_parser.c \
src/pcm_volume.c
test_software_volume_LDADD = \
$(GLIB_LIBS)
test_run_normalize_SOURCES = test/run_normalize.c \
src/audio_check.c \
src/audio_parser.c \
src/AudioCompress/compress.c
test_run_normalize_LDADD = \
$(GLIB_LIBS)
test_run_convert_SOURCES = test/run_convert.c \
src/fifo_buffer.c \
src/audio_format.c \
src/audio_check.c \
src/audio_parser.c \
src/pcm_channels.c \
src/pcm_format.c \
src/pcm_pack.c \
src/pcm_dither.c \
src/pcm_byteswap.c \
src/pcm_resample.c \
src/pcm_resample_fallback.c \
src/pcm_convert.c
test_run_convert_CPPFLAGS = $(AM_CPPFLAGS) $(SAMPLERATE_CFLAGS)
test_run_convert_LDADD = \
$(SAMPLERATE_LIBS) \
$(GLIB_LIBS)
if HAVE_LIBSAMPLERATE
test_run_convert_SOURCES += src/pcm_resample_libsamplerate.c
endif
test_run_output_CFLAGS = $(AM_CFLAGS) $(MPD_CFLAGS)
test_run_output_CPPFLAGS = $(AM_CPPFLAGS) \
$(ENCODER_CFLAGS) \
$(OUTPUT_CFLAGS)
@@ -693,7 +1017,9 @@ test_run_output_LDADD = $(MPD_LIBS) \
$(OUTPUT_LIBS) \
$(GLIB_LIBS)
test_run_output_SOURCES = test/run_output.c \
src/conf.c src/buffer2array.c src/utils.c src/log.c \
src/conf.c src/tokenizer.c src/utils.c src/log.c \
src/audio_check.c \
src/audio_format.c \
src/audio_parser.c \
src/timer.c \
src/tag.c src/tag_pool.c \
@@ -704,7 +1030,21 @@ test_run_output_SOURCES = test/run_output.c \
$(ENCODER_SRC) \
src/mixer_api.c \
src/mixer_control.c \
src/mixer_type.c \
$(MIXER_SRC) \
src/filter_plugin.c src/filter/chain_filter_plugin.c \
src/filter_config.c \
src/filter/autoconvert_filter_plugin.c \
src/filter/convert_filter_plugin.c \
src/filter/replay_gain_filter_plugin.c \
src/filter/normalize_filter_plugin.c \
src/filter/volume_filter_plugin.c \
src/pcm_volume.c \
src/AudioCompress/compress.c \
src/replay_gain_info.c \
src/replay_gain_config.c \
src/fd_util.c \
src/server_socket.c \
$(OUTPUT_SRC)
test_read_mixer_CPPFLAGS = $(AM_CPPFLAGS) \
@@ -713,10 +1053,34 @@ test_read_mixer_LDADD = $(MPD_LIBS) \
$(OUTPUT_LIBS) \
$(GLIB_LIBS)
test_read_mixer_SOURCES = test/read_mixer.c \
src/conf.c src/buffer2array.c src/utils.c src/log.c \
src/conf.c src/tokenizer.c src/utils.c src/log.c \
src/mixer_control.c src/mixer_api.c \
src/filter_plugin.c \
src/filter/volume_filter_plugin.c \
src/fd_util.c \
$(MIXER_SRC)
if ENABLE_BZIP2_TEST
TESTS += test/test_archive_bzip2.sh
endif
if ENABLE_ZZIP_TEST
TESTS += test/test_archive_zzip.sh
endif
if ENABLE_ISO9660_TEST
TESTS += test/test_archive_iso9660.sh
endif
if ENABLE_INOTIFY
noinst_PROGRAMS += test/run_inotify
test_run_inotify_SOURCES = test/run_inotify.c \
src/fd_util.c \
src/fifo_buffer.c \
src/inotify_source.c
test_run_inotify_LDADD = $(GLIB_LIBS)
endif
endif
@@ -727,7 +1091,7 @@ endif
man_MANS = doc/mpd.1 doc/mpd.conf.5
doc_DATA = AUTHORS COPYING NEWS README UPGRADING doc/mpdconf.example
DOCBOOK_FILES = doc/protocol.xml doc/user.xml doc/developer.xml doc/sticker.xml
DOCBOOK_FILES = doc/protocol.xml doc/user.xml doc/developer.xml
if ENABLE_DOCUMENTATION
protocoldir = $(docdir)/protocol
@@ -754,7 +1118,8 @@ endif
doc/api/html/index.html: doc/doxygen.conf
@mkdir -p $(@D)
$(DOXYGEN) $<
[ "$(srcdir)" = "." ] || sed '/INPUT *=/ s/\([^ ]\+\/\)/$(subst /,\/,$(srcdir))\/\1/g' $(srcdir)/doc/doxygen.conf >doc/doxygen.conf
$(DOXYGEN) doc/doxygen.conf
all-local: $(DOCBOOK_HTML) doc/api/html/index.html
@@ -764,10 +1129,12 @@ clean-local:
install-data-local: doc/api/html/index.html
$(mkinstalldirs) $(DESTDIR)$(docdir)/api/html
$(INSTALL_DATA) -c -m 644 doc/api/html/*.html doc/api/html/*.css \
doc/api/html/*.png doc/api/html/*.gif \
$(INSTALL_DATA) -c -m 644 doc/api/html/*.* \
$(DESTDIR)$(docdir)/api/html
uninstall-local:
rm -f $(DESTDIR)$(docdir)/api/html/*.*
upload: $(DOCBOOK_HTML) doc/api/html/index.html
rsync -vpruz --delete doc/ cirrus@www.musicpd.org:/var/www/musicpd.org/www/doc/ \
--chmod=Dug+rwx,Do+rx,Fug+rw,Fo+r \

162
NEWS
View File

@@ -1,3 +1,165 @@
ver 0.16 (20??/??/??)
* protocol:
- send song modification time to client
- added "update" idle event
- removed the deprecated "volume" command
- added the "findadd" command
- range support for "delete"
- "previous" really plays the previous song
- "addid" with negative position is deprecated
- "load" supports remote playlists (extm3u, pls, asx, xspf, lastfm://)
- allow changing replay gain mode on-the-fly
- omitting the range end is possible
- "update" checks if the path is malformed
* archive:
- iso: renamed plugin to "iso9660"
- zip: renamed plugin to "zzip"
* input:
- lastfm: obsolete plugin removed
- ffmpeg: new input plugin using libavformat's "avio" library
* tags:
- added tags "ArtistSort", "AlbumArtistSort"
- id3: revised "performer" tag support
- ape: MusicBrainz tags
* decoders:
- don't try a plugin twice (MIME type & suffix)
- don't fall back to "mad" unless no plugin matches
- ffmpeg: support multiple tags
- ffmpeg: convert metadata to generic format
- ffmpeg: implement the libavutil log callback
- sndfile: new decoder plugin based on libsndfile
- flac: moved CUE sheet support to a playlist plugin
- flac: support streams without STREAMINFO block
- mikmod: sample rate is configurable
- mpg123: new decoder plugin based on libmpg123
- sidplay: support sub-tunes
- sidplay: implemented songlength database
- sidplay: support seeking
- sidplay: play monaural SID tunes in mono
- sidplay: play mus, str, prg, x00 files
- wavpack: activate 32 bit support
- wavpack: allow more than 2 channels
- mp4ff: rename plugin "mp4" to "mp4ff"
* encoders:
- twolame: new encoder plugin based on libtwolame
- flac: new encoder plugin based on libFLAC
- wave: new encoder plugin for PCM WAV format
* output:
- recorder: new output plugin for recording radio streams
- alsa: don't recover on CANCEL
- alsa: fill period buffer with silence before draining
- openal: new output plugin
- pulse: announce "media.role=music"
- pulse: renamed context to "Music Player Daemon"
- pulse: connect to server on MPD startup, implement pause
- jack: require libjack 0.100
- jack: don't disconnect during pause
- jack: connect to server on MPD startup
- jack: added options "client_name", "server_name"
- jack: clear ring buffers before activating
- jack: renamed option "ports" to "destination_ports"
- jack: support more than two audio channels
- httpd: bind port when output is enabled
- httpd: added name/genre/website configuration
- httpd: implement "pause"
- httpd: bind_to_address support (including IPv6)
- oss: 24 bit support via OSS4
- win32: new output plugin for Windows Wave
- shout, httpd: more responsive to control commands
- wildcards allowed in audio_format configuration
- consistently lock audio output objects
* player:
- drain audio outputs at the end of the playlist
* mixers:
- removed support for legacy mixer configuration
- reimplemented software volume as mixer+filter plugin
- per-device software/hardware mixer setting
* commands:
- added new "status" line with more precise "elapsed time"
* update:
- automatically update the database with Linux inotify
- support .mpdignore files in the music directory
- sort songs by album name first, then disc/track number
- rescan after metadata_to_use change
* normalize: upgraded to AudioCompress 2.0
- automatically convert to 16 bit samples
* replay gain:
- reimplemented as a filter plugin
- fall back to track gain if album gain is unavailable
- optionally use hardware mixer to apply replay gain
- added mode "auto"
* log unused/unknown block parameters
* removed the deprecated "error_file" option
* save state when stopped
* renamed option "--stdout" to "--stderr"
* removed options --create-db and --no-create-db
* state_file: save only if something has changed
* database: eliminated maximum line length
* log: redirect stdout/stderr to /dev/null if syslog is used
* set the close-on-exec flag on all file descriptors
* pcm_volume, pcm_mix: implemented 32 bit support
* support packed 24 bit samples
* CUE sheet support
* support for MixRamp tags
* obey $(sysconfdir) for default mpd.conf location
* build with large file support by default
* added test suite ("make check")
* require GLib 2.12
* added libwrap support
* make single mode 'sticky'
ver 0.15.14 (2010/11/06)
* player_thread: fix assertion failure due to wrong music pipe on seek
* output_thread: fix assertion failure due to race condition in OPEN
* input:
- rewind: fix double free bug
* decoders:
- mp4ff, ffmpeg: add extension ".m4b" (audio book)
ver 0.15.13 (2010/10/10)
* output_thread: fix race condition after CANCEL command
* output:
- httpd: fix random data in stream title
- httpd: MIME type audio/ogg for Ogg Vorbis
* input:
- rewind: update MIME not only once
- rewind: enable for MMS
ver 0.15.12 (2010/07/20)
* input:
- curl: remove assertion after curl_multi_fdset()
* tags:
- rva2: set "gain", not "peak"
* decoders:
- wildmidi: support version 0.2.3
ver 0.15.11 (2010/06/14)
* tags:
- ape: support album artist
* decoders:
- mp4ff: support tags "album artist", "albumartist", "band"
- mikmod: fix memory leak
- vorbis: handle uri==NULL
- ffmpeg: fix memory leak
- ffmpeg: free AVFormatContext on error
- ffmpeg: read more metadata
- ffmpeg: fix libavformat 0.6 by using av_open_input_stream()
* playlist: emit IDLE_OPTIONS when resetting single mode
* listen: make get_remote_uid() work on BSD
ver 0.15.10 (2010/05/30)
* input:
- mms: fix memory leak in error handler
- mms: initialize the "eof" attribute
* decoders:
- mad: properly calculate ID3 size without libid3tag
ver 0.15.9 (2010/03/21)
* decoders:
- mad: fix crash when seeking at end of song

View File

@@ -9,20 +9,20 @@ srcdir="`dirname $0`"
test -z "$srcdir" && srcdir=.
cd "$srcdir"
DIE=
AM_VERSIONGREP="sed -e s/.*[^0-9\.]\([0-9]\.[0-9]\).*/\1/"
AM_VERSIONGREP="sed -e s/.*[^0-9\.]\([0-9]\.[0-9][0-9]*\).*/\1/"
AC_VERSIONGREP="sed -e s/.*[^0-9\.]\([0-9]\.[0-9][0-9]\).*/\1/"
VERSIONMKINT="sed -e s/[^0-9]//"
if test -n "$AM_FORCE_VERSION"
then
AM_VERSIONS="$AM_FORCE_VERSION"
else
AM_VERSIONS='1.9 1.10'
AM_VERSIONS='1.11 1.10'
fi
if test -n "$AC_FORCE_VERSION"
then
AC_VERSIONS="$AC_FORCE_VERSION"
else
AC_VERSIONS='2.58 2.59 2.60 2.61'
AC_VERSIONS='2.60 2.61'
fi
versioned_bins ()

File diff suppressed because it is too large Load Diff

View File

@@ -534,7 +534,7 @@ WARN_LOGFILE =
# directories like "/usr/src/myproject". Separate the files or directories
# with spaces.
INPUT = src
INPUT = src/
# This tag can be used to specify the character encoding of the source files
# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is

View File

@@ -25,17 +25,11 @@ Output a brief help message.
Kill the currently running mpd session. The pid_file parameter must be
specified in the config file for this to work.
.TP
.BI --create-db
Force (re)creation of database.
.TP
.BI --no-create-db
Do not create database, even if it doesn't exist.
.TP
.BI --no-daemon
Don't detach from console.
.TP
.BI --stdout
Print messages to stdout and stderr.
.BI --stderr
Print messages stderr.
.TP
.BI --verbose
Verbose logging.

View File

@@ -129,6 +129,8 @@ audio that is sent to each audio output. Note that audio outputs may specify
their own audio format which will be used for actual output to the audio
device. An example is "44100:16:2" for 44100Hz, 16 bits, and 2 channels. The
default is to use the audio format of the input file.
Any of the three attributes may be an asterisk to specify that this
attribute should not be enforced
.TP
.B samplerate_converter <integer or prefix>
This specifies the libsamplerate converter to use. The supplied value should
@@ -168,31 +170,14 @@ only choice) if MPD was compiled without libsamplerate.
For an up-to-date list of available converters, please see the libsamplerate
documentation (available online at <\fBhttp://www.mega-nerd.com/SRC/\fP>).
.TP
.B mixer_type <alsa, oss, software, hardware or disabled>
This specifies which mixer to use. The default is hardware and depends on
what audio output support mpd was built with. Options alsa and oss are
legacy and should not be used in new configs, but when set mixer_device
and mixer_control will apply.
.TP
.B mixer_device <mixer dev>
This specifies which mixer to use. The default for oss is
"/dev/mixer"; the default for alsa is "default". This global option is
deprecated and should not be used. Look at the mixer_device option of
corresponding output device instead.
.TP
.B mixer_control <mixer ctrl>
This specifies which mixer control to use (sometimes referred to as
the "device"). Examples of mixer controls are PCM, Line1, Master,
etc. An example for OSS is "Pcm", and an example for alsa is
"PCM". This global option is deprecated and should not be used. Look
at the mixer_control option of corresponding output device instead.
.TP
.B replaygain <album or track>
.B replaygain <off or album or track or auto>
If specified, mpd will adjust the volume of songs played using ReplayGain tags
(see <\fBhttp://www.replaygain.org/\fP>). Setting this to "album" will adjust
volume using the album's ReplayGain tags, while setting it to "track" will
adjust it using the track ReplayGain tags. Currently only FLAC, Ogg Vorbis,
Musepack, and MP3 (through ID3v2 ReplayGain tags, not APEv2) are supported.
adjust it using the track ReplayGain tags. "auto" uses the track ReplayGain
tags if random play is activated otherwise the album ReplayGain tags. Currently
only FLAC, Ogg Vorbis, Musepack, and MP3 (through ID3v2 ReplayGain tags, not
APEv2) are supported.
.TP
.B replaygain_preamp <-15 to 15>
This is the gain (in dB) applied to songs with ReplayGain tags.
@@ -265,6 +250,15 @@ tags may be specified as a comma separated list. An example value is
"artist,album,title,track". The special value "none" may be used alone to
disable all metadata. The default is to use all known tag types except for
comments.
.TP
.B auto_update <yes or no>
This specifies the wheter to support automatic update of music database when
files are changed in music_directory. The default is to disable autoupdate
of database.
.TP
.B auto_update_depth <N>
Limit the depth of the directories being watched, 0 means only watch
the music directory itself. There is no limit by default.
.SH REQUIRED AUDIO OUTPUT PARAMETERS
.TP
.B type <type>
@@ -280,11 +274,25 @@ This specifies the sample rate, bits per sample, and number of channels of
audio that is sent to the audio output device. See documentation for the
\fBaudio_output_format\fP parameter for more details. The default is to use
whatever audio format is passed to the audio output.
Any of the three attributes may be an asterisk to specify that this
attribute should not be enforced
.TP
.B replay_gain_handler <software, mixer or none>
Specifies how replay gain is applied. The default is "software",
which uses an internal software volume control. "mixer" uses the
configured (hardware) mixer control. "none" disables replay gain on
this audio output.
.SH OPTIONAL ALSA OUTPUT PARAMETERS
.TP
.B device <dev>
This specifies the device to use for audio output. The default is "default".
.TP
.B mixer_type <hardware, software or none>
Specifies which mixer should be used for this audio output: the
hardware mixer (available for ALSA, OSS and PulseAudio), the software
mixer or no mixer ("none"). By default, the hardware mixer is used
for devices which support it, and none for the others.
.TP
.B mixer_device <mixer dev>
This specifies which mixer to use. The default is "default". To use
the second sound card in a system, use "hw:1".
@@ -352,13 +360,12 @@ after another until it successfully establishes a connection.
.TP
.B sink <sink>
The sink to output to. The default is to let PulseAudio choose a sink.
.SH REQUIRED JACK OUTPUT PARAMETERS
.SH OPTIONAL JACK OUTPUT PARAMETERS
.TP
.B name <name>
.B client_name <name>
The client name to use when connecting to JACK. The output ports <name>:left
and <name>:right will also be created for the left and right channels,
respectively.
.SH OPTIONAL JACK OUTPUT PARAMETERS
.TP
.B ports <left_port,right_port>
This specifies the left and right ports to connect to for the left and right

View File

@@ -49,6 +49,11 @@
#
#state_file "~/.mpd/state"
#
# The location of the sticker database. This is a database which
# manages dynamic information attached to songs.
#
#sticker_file "~/.mpd/sticker.sql"
#
###############################################################################
@@ -61,6 +66,13 @@
#
#user "nobody"
#
# This setting specifies the group that MPD will run as. If not specified
# primary group of user specified with "user" setting will be used (if set).
# This is useful if MPD needs to be a member of group such as "audio" to
# have permission to use sound card.
#
#group "nogroup"
#
# This setting sets the address for the daemon to listen on. Careful attention
# should be paid if this is assigned to anything other then the default, any.
# This setting can deny access to control of the daemon.
@@ -102,6 +114,16 @@
#
#metadata_to_use "artist,album,title,track,name,genre,date,composer,performer,disc"
#
# This setting enables automatic update of MPD's database when files in
# music_directory are changed.
#
#auto_update "yes"
#
# Limit the depth of the directories being watched, 0 means only watch
# the music directory itself. There is no limit by default.
#
#auto_update_depth "3"
#
###############################################################################
@@ -179,6 +201,7 @@ input {
# name "My ALSA Device"
## device "hw:0,0" # optional
## format "44100:16:2" # optional
## mixer_type "hardware" # optional
## mixer_device "default" # optional
## mixer_control "PCM" # optional
## mixer_index "0" # optional
@@ -191,6 +214,7 @@ input {
# name "My OSS Device"
## device "/dev/dsp" # optional
## format "44100:16:2" # optional
## mixer_type "hardware" # optional
## mixer_device "/dev/mixer" # optional
## mixer_control "PCM" # optional
#}
@@ -214,6 +238,19 @@ input {
## genre "jazz" # optional
## public "no" # optional
## timeout "2" # optional
## mixer_type "software" # optional
#}
#
# An example of a recorder output:
#
#audio_output {
# type "recorder"
# name "My recorder"
# encoder "vorbis" # optional, vorbis or lame
# path "/var/lib/mpd/recorder/mpd.ogg"
## quality "5.0" # do not define if bitrate is defined
# bitrate "128" # do not define if quality is defined
# format "44100:16:1"
#}
#
# An example of a httpd output (built-in HTTP streaming server):
@@ -223,9 +260,11 @@ input {
# name "My HTTP Stream"
# encoder "vorbis" # optional, vorbis or lame
# port "8000"
# bind_to_address "0.0.0.0" # optional, IPv4 or IPv6
## quality "5.0" # do not define if bitrate is defined
# bitrate "128" # do not define if quality is defined
# format "44100:16:1"
# max_clients "0" # optional 0=no limit
#}
#
# An example of a pulseaudio output (streaming to a remote pulseaudio server)
@@ -255,6 +294,7 @@ input {
#audio_output {
# type "null"
# name "My Null Output"
# mixer_type "none" # optional
#}
#
# This setting will change all decoded audio to be converted to the specified
@@ -273,38 +313,11 @@ input {
###############################################################################
# Volume control mixer ########################################################
#
# These are the global volume control settings. By default, this setting will
# be detected to the available audio output device, with preference going to
# hardware mixing. Hardware and software mixers for individual audio_output
# sections cannot yet be mixed.
#
# An example for controlling an ALSA, OSS or Pulseaudio mixer; If this
# setting is used other sound applications will be affected by the volume
# being controlled by MPD.
#
#mixer_type "hardware"
#
# An example for controlling all mixers through software. This will control
# all controls, even if the mixer is not supported by the device and will not
# affect any other sound producing applications.
#
#mixer_type "software"
#
# This example will not allow MPD to touch the mixer at all and will disable
# all volume controls.
#
#mixer_type "disabled"
#
###############################################################################
# Normalization automatic volume adjustments ##################################
#
# This setting specifies the type of ReplayGain to use. This setting can have
# the argument "album" or "track". See <http://www.replaygain.org> for more
# details. This setting is disabled by default.
# the argument "off", "album" or "track". See <http://www.replaygain.org>
# for more details. This setting is off by default.
#
#replaygain "album"
#
@@ -357,8 +370,7 @@ input {
# Character Encoding ##########################################################
#
# If file or directory names do not display correctly for your locale then you
# may need to modify this setting. After modification of this setting mpd
# --create-db must be run to change the database.
# may need to modify this setting.
#
#filesystem_charset "UTF-8"
#
@@ -367,3 +379,29 @@ input {
#id3v1_encoding "ISO-8859-1"
#
###############################################################################
# SIDPlay decoder #############################################################
#
# songlength_database:
# Location of your songlengths file, as distributed with the HVSC.
# The sidplay plugin checks this for matching MD5 fingerprints.
# See http://www.c64.org/HVSC/DOCUMENTS/Songlengths.faq
#
# default_songlength:
# This is the default playing time in seconds for songs not in the
# songlength database, or in case you're not using a database.
# A value of 0 means play indefinitely.
#
# filter:
# Turns the SID filter emulation on or off.
#
#decoder {
# plugin "sidplay"
# songlength_database "/media/C64Music/DOCUMENTS/Songlengths.txt"
# default_songlength "120"
# filter "true"
#}
#
###############################################################################

View File

@@ -67,6 +67,20 @@
successful command executed in the command list.
</para>
</section>
<section>
<title>Ranges</title>
<para>
Some commands (e.g. <link
linkend="command_delete"><command>delete</command></link>)
allow specifying a range in the form
<parameter>START:END</parameter> (the <varname>END</varname>
item is not included in the range, similar to ranges in the
Python programming language). If <varname>END</varname> is
omitted, then the maximum possible value is assumed.
</para>
</section>
</chapter>
<chapter>
@@ -123,7 +137,7 @@
</term>
<listitem>
<para>
<footnote id="since_0_14"><simpara>Since MPD 0.14</simpara></footnote>
<footnote id="since_0_14"><simpara>Introduced with MPD 0.14</simpara></footnote>
Waits until there is a noteworthy change in one or more
of MPD's subsystems. As soon as there is one, it lists
all changed systems in a line in the format
@@ -137,6 +151,15 @@
has been modified after <command>update</command>.
</para>
</listitem>
<listitem>
<para>
<returnvalue>update</returnvalue>: a database update
has started or finished. If the database was
modified during the update, the
<returnvalue>database</returnvalue> event is also
emitted.
</para>
</listitem>
<listitem>
<para>
<returnvalue>stored_playlist</returnvalue>: a stored
@@ -172,7 +195,7 @@
<para>
<returnvalue>options</returnvalue>: options like
<option>repeat</option>, <option>random</option>,
<option>crossfade</option>
<option>crossfade</option>, replay gain
</para>
</listitem>
</itemizedlist>
@@ -191,9 +214,6 @@
MPD will only send notifications when something changed in
one of the specified subsytems.
</para>
<simpara>
Since <application>MPD</application> 0.14
</simpara>
</listitem>
</varlistentry>
<varlistentry id="command_status">
@@ -222,16 +242,22 @@
</listitem>
<listitem>
<para>
<varname>single</varname>:
<varname>random</varname>:
<returnvalue>0 or 1</returnvalue>
</para>
</listitem>
<listitem>
<para>
<varname>single</varname>:
<footnote id="since_0_15"><simpara>Introduced with MPD 0.15</simpara></footnote>
<returnvalue>0 or 1</returnvalue>
<footnote id="since_0_15"><simpara>Since MPD 0.15</simpara></footnote>
</para>
</listitem>
<listitem>
<para>
<varname>consume</varname>:
<returnvalue>0 or 1</returnvalue>
<footnoteref linkend="since_0_15"/>
<returnvalue>0 or 1</returnvalue>
</para>
</listitem>
<listitem>
@@ -295,6 +321,16 @@
playing/paused song)</returnvalue>
</para>
</listitem>
<listitem>
<para>
<varname>elapsed</varname>:
<footnote id="since_0_16"><simpara>Introduced with MPD 0.16</simpara></footnote>
<returnvalue>
Total time elapsed within the current song, but
with higher resolution.
</returnvalue>
</para>
</listitem>
<listitem>
<para>
<varname>bitrate</varname>:
@@ -308,6 +344,18 @@
<returnvalue>crossfade in seconds</returnvalue>
</para>
</listitem>
<listitem>
<para>
<varname>mixrampdb</varname>:
<returnvalue>mixramp threshold in dB</returnvalue>
</para>
</listitem>
<listitem>
<para>
<varname>mixrampdelay</varname>:
<returnvalue>mixrampdelay in seconds</returnvalue>
</para>
</listitem>
<listitem>
<para>
<varname>audio</varname>:
@@ -412,6 +460,32 @@
</para>
</listitem>
</varlistentry>
<varlistentry id="command_mixrampdb">
<term>
<cmdsynopsis>
<command>mixrampdb</command>
<arg choice="req"><replaceable>deciBels</replaceable></arg>
</cmdsynopsis>
</term>
<listitem>
<para>
Sets the threshold at which songs will be overlapped. Like crossfading but doesn't fade the track volume, just overlaps. The songs need to have MixRamp tags added by an external tool. 0dB is the normalized maximum volume so use negative values, I prefer -17dB. In the absence of mixramp tags crossfading will be used. See http://sourceforge.net/projects/mixramp
</para>
</listitem>
</varlistentry>
<varlistentry id="command_mixrampdelay">
<term>
<cmdsynopsis>
<command>mixrampdelay</command>
<arg choice="req"><replaceable>SECONDS</replaceable></arg>
</cmdsynopsis>
</term>
<listitem>
<para>
Additional time subtracted from the overlap calculated by mixrampdb. A value of "nan" disables MixRamp overlapping and falls back to crossfading.
</para>
</listitem>
</varlistentry>
<varlistentry id="command_random">
<term>
<cmdsynopsis>
@@ -471,23 +545,43 @@
</para>
</listitem>
</varlistentry>
<varlistentry id="command_volume">
<varlistentry id="command_replay_gain_mode">
<term>
<cmdsynopsis>
<command>volume</command>
<arg choice="req"><replaceable>CHANGE</replaceable></arg>
<command>replay_gain_mode</command>
<arg choice="req"><replaceable>MODE</replaceable></arg>
</cmdsynopsis>
</term>
<listitem>
<para>
Changes volume by amount <varname>CHANGE</varname>.
Sets the replay gain mode. One of
<parameter>off</parameter>,
<parameter>track</parameter>,
<parameter>album</parameter>.
</para>
<note>
<para>
<command>volume</command> is deprecated, use
<command>setvol</command> instead.
Changing the mode during playback may take several
seconds, because the new settings does not affect the
buffered data.
</para>
<para>
This command triggers the
<returnvalue>options</returnvalue> idle event.
</para>
</listitem>
</varlistentry>
<varlistentry id="command_replay_gain_status">
<term>
<cmdsynopsis>
<command>replay_gain_status</command>
</cmdsynopsis>
</term>
<listitem>
<para>
Prints replay gain options. Currently, only the
variable <varname>replay_gain_mode</varname> is
returned.
</para>
</note>
</listitem>
</varlistentry>
</variablelist>
@@ -648,10 +742,7 @@
</para>
<para>
<varname>URI</varname> is always a single file or
URL. <varname>POSITION</varname> is optional, a
negative number means it is relative to the currently
playing song in the playlist (if there is one).
For example:
URL. For example:
</para>
<screen>
addid "foo.mp3"
@@ -676,7 +767,10 @@ OK
<term>
<cmdsynopsis>
<command>delete</command>
<arg choice="req"><replaceable>SONGPOS</replaceable></arg>
<group>
<arg choice="req"><replaceable>POS</replaceable></arg>
<arg choice="req"><replaceable>START:END</replaceable></arg>
</group>
</cmdsynopsis>
</term>
<listitem>
@@ -910,6 +1004,20 @@ OK
<section>
<title>Stored playlists</title>
<para>
Playlists are stored inside the configured playlist directory.
They are addressed with their file name (without the directory
and without the <filename>.m3u</filename> suffix).
</para>
<para>
Some of the commands described in this section can be used to
run playlist plugins instead of the hard-coded simple
<filename>m3u</filename> parser. They can access playlists in
the music directory (relative path including the suffix) or
remote playlists (absolute URI with a supported scheme).
</para>
<variablelist>
<varlistentry id="command_listplaylist">
<term>
@@ -920,8 +1028,8 @@ OK
</term>
<listitem>
<para>
Lists the files in the playlist
<filename>NAME.m3u</filename>.
Lists the songs in the playlist. Playlist plugins are
supported.
</para>
</listitem>
</varlistentry>
@@ -934,7 +1042,8 @@ OK
</term>
<listitem>
<para>
Lists songs in the playlist <filename>NAME.m3u</filename>.
Lists the songs with metadata in the playlist. Playlist
plugins are supported.
</para>
</listitem>
</varlistentry>
@@ -966,8 +1075,8 @@ OK
</term>
<listitem>
<para>
Loads the playlist <filename>NAME.m3u</filename> from
the playlist directory.
Loads the playlist into the current queue. Playlist
plugins are supported.
</para>
</listitem>
</varlistentry>
@@ -1105,16 +1214,35 @@ OK
<command>find</command>
<arg choice="req"><replaceable>TYPE</replaceable></arg>
<arg choice="req"><replaceable>WHAT</replaceable></arg>
<arg choice="opt"><replaceable>...</replaceable></arg>
</cmdsynopsis>
</term>
<listitem>
<para>
Finds songs in the db that are exactly
<varname>WHAT</varname>. <varname>TYPE</varname> should
be <parameter>album</parameter>,
<parameter>artist</parameter>, or
<parameter>title</parameter>. <varname>WHAT</varname>
is what to find.
<varname>WHAT</varname>. <varname>TYPE</varname> can
be any tag supported by MPD, or one of the two special
parameters — <parameter>file</parameter> to search by
full path (relative to database root), and
<parameter>any</parameter> to match against all
available tags. <varname>WHAT</varname> is what to find.
</para>
</listitem>
</varlistentry>
<varlistentry id="command_findadd">
<term>
<cmdsynopsis>
<command>findadd</command>
<arg choice="req"><replaceable>TYPE</replaceable></arg>
<arg choice="req"><replaceable>WHAT</replaceable></arg>
<arg choice="opt"><replaceable>...</replaceable></arg>
</cmdsynopsis>
</term>
<listitem>
<para>
Finds songs in the db that are exactly
<varname>WHAT</varname> and adds them to current playlist.
Parameters have the same meaning as for <command>find</command>.
</para>
</listitem>
</varlistentry>
@@ -1129,7 +1257,8 @@ OK
<listitem>
<para>
Lists all tags of the specified type.
<varname>TYPE</varname> should be album or artist.
<varname>TYPE</varname> can be any tag supported by MPD or
<parameter>file</parameter>.
</para>
<para>
<varname>ARTIST</varname> is an optional parameter when
@@ -1192,17 +1321,15 @@ OK
<command>search</command>
<arg choice="req"><replaceable>TYPE</replaceable></arg>
<arg choice="req"><replaceable>WHAT</replaceable></arg>
<arg choice="opt"><replaceable>...</replaceable></arg>
</cmdsynopsis>
</term>
<listitem>
<para>
Searches for any song that contains
<varname>WHAT</varname>. <varname>TYPE</varname> can be
<parameter>title</parameter>,
<parameter>artist</parameter>,
<parameter>album</parameter> or
<parameter>filename</parameter>. Search is not case
sensitive.
<varname>WHAT</varname>. Parameters have the same meaning
as for <command>find</command>, except that search is not
case sensitive.
</para>
</listitem>
</varlistentry>
@@ -1231,6 +1358,20 @@ OK
</para>
</listitem>
</varlistentry>
<varlistentry id="command_rescan">
<term>
<cmdsynopsis>
<command>rescan</command>
<arg choice="opt"><replaceable>URI</replaceable></arg>
</cmdsynopsis>
</term>
<listitem>
<para>
Same as <command>update</command>, but also rescans
unmodified files.
</para>
</listitem>
</varlistentry>
</variablelist>
</section>
@@ -1508,6 +1649,25 @@ OK
</para>
</listitem>
</varlistentry>
<varlistentry id="command_decoders">
<term>
<cmdsynopsis>
<command>decoders</command>
</cmdsynopsis>
</term>
<listitem>
<para>
Print a list of decoder plugins, followed by their
supported suffixes and MIME types. Example response:
</para>
<programlisting>plugin: mad
suffix: mp3
suffix: mp2
mime_type: audio/mpeg
plugin: mpcdec
suffix: mpc</programlisting>
</listitem>
</varlistentry>
</variablelist>
</section>
</chapter>

View File

@@ -1,91 +0,0 @@
<?xml version='1.0' encoding="utf-8"?>
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"docbook/dtd/xml/4.2/docbookx.dtd">
<book>
<title>The Music Player Daemon Sticker Database</title>
<chapter>
<title>Introduction to MPD's Sticker Database</title>
<para>
This document shell give a short guideline for recommended tags
for use in MPD's Sticker Database.
MPD's Sticker Database is a subsystem that enables users to add
custom tags. MPD does not alter the media files.
</para>
</chapter>
<chapter>
<title>Guideline for recommended tags</title>
<para>
Since there is no standard for tags in media files, this
document is trying to give you some help deciding what tags to
use. The selection of these tags tries to cover the most
widely used tags. This way the tags might still work in other
players, if you sync the database with your original media
files.
Keep in mind that we stick with lower case tags with underscores
instead of spaces. If there will be a Sync tool in future
its easy to change this on the fly, if needed.
</para>
<variablelist>
<varlistentry>
<term><varname>rating</varname></term>
<listitem>
<para>
Will store a rating value from 1 (worst) to 5 (best) for a
given song.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>album_rating</varname></term>
<listitem>
<para>
Will store a rating value from 1 (worst) to 5 (best) for a
given album.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>style</varname></term>
<listitem>
<para>
This tag is used to keep the Genre tag clean, by now
having 1000's of genres. Instead you define a Main Genre
for each file and can make a more specific
description. This should be one Keyword like "Post Punk"
or "Progressive Death Metal" An Alternative name for this
tag is "Subgenre", time will tell which one gets more
support.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>lyrics</varname></term>
<listitem>
<para>
This one is self explaining. This gives the option to
store lyrics of a song where they belong to: mapped to the
song
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>similar_artists</varname> (Comma seperated list of artists)</term>
<listitem>
<para>
This tag enables a last.fm alike aproach which will still
work when being offline Keep in mind, that this tag is
absolutely non-standard! I am not aware of any other
player that uses a comparable tag.
</para>
</listitem>
</varlistentry>
</variablelist>
</chapter>
</book>

View File

@@ -300,10 +300,30 @@ cd mpd-version</programlisting>
<varname>format</varname>
</entry>
<entry>
<para>
Always open the audio output with the specified audio
format (samplerate:bits:channels), regardless of the
format of the input file. This is optional for most
plugins.
</para>
<para>
Any of the three attributes may be an asterisk to
specify that this attribute should not be enforced,
example: <parameter>48000:16:*</parameter>.
<parameter>*:*:*</parameter> is equal to not having
a <varname>format</varname> specification.
</para>
<para>
The following values are valid for
<varname>bits</varname>: <varname>8</varname>
(signed 8 bit integer samples),
<varname>16</varname>, <varname>24</varname> (signed
24 bit integer samples padded to 32 bit),
<varname>24_3</varname> (signed 24 bit integer
samples, no padding, 3 bytes per sample),
<varname>32</varname> (signed 32 bit integer
samples).
</para>
</entry>
</row>
<row>
@@ -319,19 +339,227 @@ cd mpd-version</programlisting>
</row>
<row>
<entry>
<varname>mixer_enabled</varname>
<varname>always_on</varname>
<parameter>yes|no</parameter>
</entry>
<entry>
Specifies whether the hardware mixer of this audio
output should be used. By default, all hardware
mixers are enabled if available.
If set to "yes", then MPD attempts to keep this audio
output always open. This may be useful for streaming
servers, when you don't want to disconnect all
listeners even when playback is accidently stopped.
</entry>
</row>
<row>
<entry>
<varname>mixer_type</varname>
<parameter>hardware|software|none</parameter>
</entry>
<entry>
Specifies which mixer should be used for this audio
output: the hardware mixer (available for ALSA, OSS
and PulseAudio), the software mixer or no mixer
("none"). By default, the hardware mixer is used for
devices which support it, and none for the others.
</entry>
</row>
<row>
<entry>
<varname>replay_gain_handler</varname>
<parameter>software|mixer|none</parameter>
</entry>
<entry>
Specifies how replay gain is applied. The default is
"software", which uses an internal software volume
control. "mixer" uses the configured (hardware) mixer
control. "none" disables replay gain on this audio
output.
</entry>
</row>
</tbody>
</tgroup>
</informaltable>
</section>
<section>
<title>Configuring filters</title>
<para>
Filters are plugins which modify an audio stream.
</para>
<para>
To configure a filter, add a <varname>filter</varname> block
to <filename>mpd.conf</filename>:
</para>
<programlisting>filter {
plugin "volume"
name "software volume"
}
</programlisting>
<para>
The following table lists the <varname>filter</varname>
options valid for all plugins:
</para>
<informaltable>
<tgroup cols="2">
<thead>
<row>
<entry>
Name
</entry>
<entry>
Description
</entry>
</row>
</thead>
<tbody>
<row>
<entry>
<varname>plugin</varname>
</entry>
<entry>
The name of the plugin.
</entry>
</row>
<row>
<entry>
<varname>name</varname>
</entry>
<entry>
The name of the filter.
</entry>
</row>
</tbody>
</tgroup>
</informaltable>
</section>
<section>
<title>Configuring playlist plugins</title>
<para>
Playlist plugins are used to load remote playlists. This is
not related to MPD's playlist directory.
</para>
<para>
To configure a filter, add a
<varname>playlist_plugin</varname> block to
<filename>mpd.conf</filename>:
</para>
<programlisting>playlist_plugin {
name "m3u"
enabled "true"
}
</programlisting>
<para>
The following table lists the
<varname>playlist_plugin</varname> options valid for all
plugins:
</para>
<informaltable>
<tgroup cols="2">
<thead>
<row>
<entry>
Name
</entry>
<entry>
Description
</entry>
</row>
</thead>
<tbody>
<row>
<entry>
<varname>name</varname>
</entry>
<entry>
The name of the plugin.
</entry>
</row>
<row>
<entry>
<varname>enabled</varname>
<parameter>yes|no</parameter>
</entry>
<entry>
Allows you to disable a input plugin without
recompiling. By default, all plugins are enabled.
</entry>
</row>
</tbody>
</tgroup>
</informaltable>
</section>
</chapter>
<chapter>
<title>Using MPD</title>
<section>
<title>The client</title>
<para>
After you have installed, configured and started MPD, you
choose a client to control the playback.
</para>
<para>
The most basic client is <filename>mpc</filename>, which
provides a command line interface. It is useful in shell
scripts. Many people bind specific <filename>mpc</filename>
commands to hotkeys.
</para>
<para>
The <ulink url="http://mpd.wikia.com/wiki/Clients">MPD
Wiki</ulink> contains an extensive list of clients to choose
from.
</para>
</section>
<section>
<title>The music directory and the database</title>
<para>
The "music directory" is where you store your music files.
MPD stores all relevant meta information about all songs in
its "database". Whenever you add, modify or remove songs in
the music directory, you have to update the database, for
example with <filename>mpc</filename>:
</para>
<programlisting>mpc update</programlisting>
<para>
Depending on the size of your music collection and the speed
of the storage, this can take a while.
</para>
<para>
To exclude a file from the update, create a file called
<filename>.mpdignore</filename> in its parent directory. Each
line of that file may contain a list of shell wildcards.
</para>
</section>
<section>
<title>The queue</title>
<para>
The queue (sometimes called "current playlist") is a list of
songs to be played by MPD. To play a song, add it to the
queue and start playback. Most clients offer an interface to
edit the queue.
</para>
</section>
</chapter>
<chapter>
@@ -386,15 +614,6 @@ cd mpd-version</programlisting>
</para>
</section>
<section>
<title><varname>lastfm</varname></title>
<para>
Plays last.fm radio. This plugin is experimental, and will
be superseded by a better solution in MPD 0.16.
</para>
</section>
<section>
<title><varname>mms</varname></title>
@@ -404,6 +623,40 @@ cd mpd-version</programlisting>
</section>
</section>
<section>
<title>Decoder plugins</title>
<section>
<title><varname>mikmod</varname></title>
<para>
Module player based on MikMod.
</para>
<informaltable>
<tgroup cols="2">
<thead>
<row>
<entry>Setting</entry>
<entry>Description</entry>
</row>
</thead>
<tbody>
<row>
<entry>
<varname>sample_rate</varname>
</entry>
<entry>
Sets the sample rate generated by
<filename>libmikmod</filename>. Default is 44100.
</entry>
</row>
</tbody>
</tgroup>
</informaltable>
</section>
</section>
<section>
<title>Output plugins</title>
@@ -535,6 +788,43 @@ cd mpd-version</programlisting>
</para>
</section>
<section>
<title><varname>ffado</varname></title>
<para>
The <varname>ffado</varname> plugin connects to FireWire
audio devices via <filename>libffado</filename>.
</para>
<para>
Warning: this plugin was not tested successfully. I just
couldn't keep libffado2 from crashing. Use at your own
risk.
</para>
<informaltable>
<tgroup cols="2">
<thead>
<row>
<entry>Setting</entry>
<entry>Description</entry>
</row>
</thead>
<tbody>
<row>
<entry>
<varname>device</varname>
<parameter>NAME</parameter>
</entry>
<entry>
Sets the device which should be used, e.g. "hw:0".
</entry>
</row>
</tbody>
</tgroup>
</informaltable>
</section>
<section>
<title><varname>jack</varname></title>
@@ -542,6 +832,81 @@ cd mpd-version</programlisting>
The <varname>jack</varname> plugin connects to a JACK
server.
</para>
<informaltable>
<tgroup cols="2">
<thead>
<row>
<entry>Setting</entry>
<entry>Description</entry>
</row>
</thead>
<tbody>
<row>
<entry>
<varname>client_name</varname>
<parameter>NAME</parameter>
</entry>
<entry>
The name of the JACK client. Defaults to "Music
Player Daemon".
</entry>
</row>
<row>
<entry>
<varname>server_name</varname>
<parameter>NAME</parameter>
</entry>
<entry>
Optional name of the JACK server.
</entry>
</row>
<row>
<entry>
<varname>autostart</varname>
<parameter>yes|no</parameter>
</entry>
<entry>
If set to <parameter>yes</parameter>, then
<filename>libjack</filename> will automatically
launch the JACK daemon. Disabled by default.
</entry>
</row>
<row>
<entry>
<varname>source_ports</varname>
<parameter>A,B</parameter>
</entry>
<entry>
The names of the JACK source ports to be created.
By default, the ports "left" and "right" are
created. To use more ports, you have to tweak this
option.
</entry>
</row>
<row>
<entry>
<varname>destination_ports</varname>
<parameter>A,B</parameter>
</entry>
<entry>
The names of the JACK destination ports to connect to.
</entry>
</row>
<row>
<entry>
<varname>ringbuffer_size</varname>
<parameter>NBYTES</parameter>
</entry>
<entry>
Sets the size of the ring buffer for each channel.
Do not configure this value unless you know what
you're doing.
</entry>
</row>
</tbody>
</tgroup>
</informaltable>
</section>
<section>
@@ -586,8 +951,17 @@ cd mpd-version</programlisting>
<parameter>P</parameter>
</entry>
<entry>
Binds the HTTP server to the specified port (on all
interfaces).
Binds the HTTP server to the specified port.
</entry>
</row>
<row>
<entry>
<varname>bind_to_address</varname>
<parameter>ADDR</parameter>
</entry>
<entry>
Binds the HTTP server to the specified address (IPv4 or
IPv6). Multiple addresses in parallel are not supported.
</entry>
</row>
<row>
@@ -620,6 +994,16 @@ cd mpd-version</programlisting>
second.
</entry>
</row>
<row>
<entry>
<varname>max_clients</varname>
<parameter>MC</parameter>
</entry>
<entry>
Sets a limit, number of concurrent clients. When set
to 0 no limit will apply.
</entry>
</row>
</tbody>
</tgroup>
</informaltable>
@@ -693,6 +1077,39 @@ cd mpd-version</programlisting>
</informaltable>
</section>
<section>
<title><varname>openal</varname></title>
<para>
The "OpenAL" plugin uses <filename>libopenal</filename>.
It is supported on many platforms.
</para>
<informaltable>
<tgroup cols="2">
<thead>
<row>
<entry>Setting</entry>
<entry>Description</entry>
</row>
</thead>
<tbody>
<row>
<entry>
<varname>device</varname>
<parameter>NAME</parameter>
</entry>
<entry>
Sets the device which should be used. This can be
any valid OpenAL device name. If not specified, then
<filename>libopenal</filename> will choose a default device.
</entry>
</row>
</tbody>
</tgroup>
</informaltable>
</section>
<section>
<title><varname>osx</varname></title>
@@ -775,6 +1192,73 @@ cd mpd-version</programlisting>
</informaltable>
</section>
<section>
<title><varname>recorder</varname></title>
<para>
The <varname>recorder</varname> plugin writes the audio
played by MPD to a file. This may be useful for recording
radio streams.
</para>
<para>
You must configure either <varname>quality</varname> or
<varname>bitrate</varname>.
</para>
<informaltable>
<tgroup cols="2">
<thead>
<row>
<entry>Setting</entry>
<entry>Description</entry>
</row>
</thead>
<tbody>
<row>
<entry>
<varname>path</varname>
<parameter>P</parameter>
</entry>
<entry>
Write to this file.
</entry>
</row>
<row>
<entry>
<varname>encoder</varname>
<parameter>NAME</parameter>
</entry>
<entry>
Chooses an encoder plugin,
e.g. <parameter>vorbis</parameter>.
</entry>
</row>
<row>
<entry>
<varname>quality</varname>
<parameter>Q</parameter>
</entry>
<entry>
Configures the encoder quality (for VBR) in the
range -1 .. 10.
</entry>
</row>
<row>
<entry>
<varname>bitrate</varname>
<parameter>BR</parameter>
</entry>
<entry>
Sets a constant encoder bit rate, in kilobit per
second.
</entry>
</row>
</tbody>
</tgroup>
</informaltable>
</section>
<section>
<title><varname>shout</varname></title>
@@ -939,5 +1423,81 @@ cd mpd-version</programlisting>
</informaltable>
</section>
</section>
<section>
<title>Playlist plugins</title>
<section>
<title><varname>lastfm</varname></title>
<para>
Plays last.fm radio.
</para>
<informaltable>
<tgroup cols="2">
<thead>
<row>
<entry>Setting</entry>
<entry>Description</entry>
</row>
</thead>
<tbody>
<row>
<entry>
<varname>user</varname>
<parameter>USERNAME</parameter>
</entry>
<entry>
The last.fm user name.
</entry>
</row>
<row>
<entry>
<varname>password</varname>
<parameter>PWD</parameter>
</entry>
<entry>
The last.fm password.
</entry>
</row>
</tbody>
</tgroup>
</informaltable>
</section>
<section>
<title><varname>m3u</varname></title>
<para>
Reads <filename>.m3u</filename> playlist files.
</para>
</section>
<section>
<title><varname>extm3u</varname></title>
<para>
Reads extended <filename>.m3u</filename> playlist files.
</para>
</section>
<section>
<title><varname>pls</varname></title>
<para>
Reads <filename>.pls</filename> playlist files.
</para>
</section>
<section>
<title><varname>xspf</varname></title>
<para>
Reads <ulink url="http://www.xspf.org/">XSPF</ulink>
playlist files.
</para>
</section>
</section>
</chapter>
</book>

View File

@@ -1,111 +0,0 @@
dnl borrowed from oddsock.org
dnl AM_PATH_LAME([ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]])
dnl Test for liblame, and define LAME_CFLAGS and LAME_LIBS
dnl
AC_DEFUN([AM_PATH_LAME],
[dnl
dnl Get the cflags and libraries
dnl
AC_ARG_WITH(lame,
AS_HELP_STRING([--with-lame=PFX],
[prefix where liblame is installed (optional)]),,
lame_prefix="")
AC_ARG_WITH(lame-libraries,
AS_HELP_STRING([--with-lame-libraries=DIR],
[directory where liblame library is installed (optional)]),,
lame_libraries="")
AC_ARG_WITH(lame-includes,
AS_HELP_STRING([--with-lame-includes=DIR],
[directory where liblame header files are installed (optional)]),,
lame_includes="")
if test "x$lame_prefix" != "xno" ; then
if test "x$lame_libraries" != "x" ; then
LAME_LIBS="-L$lame_libraries"
elif test "x$lame_prefix" != "x" ; then
LAME_LIBS="-L$lame_prefix/lib"
elif test "x$prefix" != "xNONE" ; then
LAME_LIBS="-L$prefix/lib"
fi
LAME_LIBS="$LAME_LIBS -lmp3lame -lm"
if test "x$lame_includes" != "x" ; then
LAME_CFLAGS="-I$lame_includes"
elif test "x$lame_prefix" != "x" ; then
LAME_CFLAGS="-I$lame_prefix/include"
elif test "x$prefix" != "xNONE"; then
LAME_CFLAGS="-I$prefix/include"
fi
AC_MSG_CHECKING(for liblame)
no_lame=""
ac_save_CFLAGS="$CFLAGS"
ac_save_LIBS="$LIBS"
CFLAGS="$CFLAGS $LAME_CFLAGS"
LIBS="$LIBS $LAME_LIBS"
dnl
dnl Now check if the installed liblame is sufficiently new.
dnl
rm -f conf.lametest
AC_TRY_RUN([
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <lame/lame.h>
int main ()
{
system("touch conf.lametest");
return 0;
}
],, no_lame=yes,[echo $ac_n "cross compiling; assumed OK... $ac_c"])
CFLAGS="$ac_save_CFLAGS"
LIBS="$ac_save_LIBS"
fi
if test "x$no_lame" = "x" ; then
AC_MSG_RESULT(yes)
ifelse([$1], , :, [$1])
else
AC_MSG_RESULT(no)
if test -f conf.lametest ; then
:
else
echo "*** Could not run liblame test program, checking why..."
CFLAGS="$CFLAGS $LAME_CFLAGS"
LIBS="$LIBS $LAME_LIBS"
AC_TRY_LINK([
#include <stdio.h>
#include <lame/lame.h>
], [ return 0; ],
[ echo "*** The test program compiled, but did not run. This usually means"
echo "*** that the run-time linker is not finding liblame or finding the wrong"
echo "*** version of liblame. If it is not finding liblame, you'll need to set your"
echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point"
echo "*** to the installed location Also, make sure you have run ldconfig if that"
echo "*** is required on your system"
echo "***"
echo "*** If you have an old version installed, it is best to remove it, although"
echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH"],
[ echo "*** The test program failed to compile or link. See the file config.log for the"
echo "*** exact error that occured. This usually means liblame was incorrectly installed"
echo "*** or that you have moved liblame since it was installed." ])
CFLAGS="$ac_save_CFLAGS"
LIBS="$ac_save_LIBS"
fi
LAME_CFLAGS=""
LAME_LIBS=""
ifelse([$2], , :, [$2])
fi
AC_DEFINE(HAVE_LAME, 1, [Define if you have liblame.])
use_lame="1"
AC_SUBST(LAME_CFLAGS)
AC_SUBST(LAME_LIBS)
rm -f conf.lametest
])

View File

@@ -1,116 +0,0 @@
# Configure paths for libOggFLAC
# "Inspired" by ogg.m4
dnl AM_PATH_LIBOGGFLAC([ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]])
dnl Test for libOggFLAC, and define LIBOGGFLAC_CFLAGS and LIBOGGFLAC_LIBS
dnl
AC_DEFUN([AM_PATH_LIBOGGFLAC],
[dnl
dnl Get the cflags and libraries
dnl
AC_ARG_WITH(libOggFLAC,
AS_HELP_STRING([--with-libOggFLAC=PFX],
[prefix where libOggFLAC is installed (optional)]),,
libOggFLAC_prefix="")
AC_ARG_WITH(libOggFLAC-libraries,
AS_HELP_STRING([--with-libOggFLAC-libraries=DIR],
[directory where libOggFLAC library is installed (optional)]),,
libOggFLAC_libraries="")
AC_ARG_WITH(libOggFLAC-includes,
AS_HELP_STRING([--with-libOggFLAC-includes=DIR],
[directory where libOggFLAC header files are installed (optional)]),,
libOggFLAC_includes="")
AC_ARG_ENABLE(libOggFLACtest,
AS_HELP_STRING([--disable-libOggFLACtest],
[do not try to compile and run a test libOggFLAC program]),,
enable_libOggFLACtest=yes)
if test "x$libOggFLAC_libraries" != "x" ; then
LIBOGGFLAC_LIBS="-L$libOggFLAC_libraries"
elif test "x$libOggFLAC_prefix" != "x" ; then
LIBOGGFLAC_LIBS="-L$libOggFLAC_prefix/lib"
elif test "x$prefix" != "xNONE" ; then
LIBOGGFLAC_LIBS="-L$libdir"
fi
LIBOGGFLAC_LIBS="$LIBOGGFLAC_LIBS -lOggFLAC -lFLAC -lm"
if test "x$libOggFLAC_includes" != "x" ; then
LIBOGGFLAC_CFLAGS="-I$libOggFLAC_includes"
elif test "x$libOggFLAC_prefix" != "x" ; then
LIBOGGFLAC_CFLAGS="-I$libOggFLAC_prefix/include"
elif test "x$prefix" != "xNONE"; then
LIBOGGFLAC_CFLAGS="-I$prefix/include"
fi
AC_MSG_CHECKING(for libOggFLAC)
no_libOggFLAC=""
if test "x$enable_libOggFLACtest" = "xyes" ; then
ac_save_CFLAGS="$CFLAGS"
ac_save_CXXFLAGS="$CXXFLAGS"
ac_save_LIBS="$LIBS"
CFLAGS="$CFLAGS $LIBOGGFLAC_CFLAGS"
CXXFLAGS="$CXXFLAGS $LIBOGGFLAC_CFLAGS"
LIBS="$LIBS $LIBOGGFLAC_LIBS"
dnl
dnl Now check if the installed libOggFLAC is sufficiently new.
dnl
rm -f conf.libOggFLACtest
AC_TRY_RUN([
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <OggFLAC/stream_decoder.h>
int main ()
{
system("touch conf.libOggFLACtest");
return 0;
}
],, no_libOggFLAC=yes,[echo $ac_n "cross compiling; assumed OK... $ac_c"])
CFLAGS="$ac_save_CFLAGS"
LIBS="$ac_save_LIBS"
fi
if test "x$no_libOggFLAC" = "x" ; then
AC_MSG_RESULT(yes)
ifelse([$1], , :, [$1])
else
AC_MSG_RESULT(no)
if test -f conf.libOggFLACtest ; then
:
else
echo "*** Could not run libOggFLAC test program, checking why..."
CFLAGS="$CFLAGS $LIBOGGFLAC_CFLAGS"
LIBS="$LIBS $LIBOGGFLAC_LIBS"
AC_TRY_LINK([
#include <stdio.h>
#include <OggFLAC/stream_decoder.h>
], [ return 0; ],
[ echo "*** The test program compiled, but did not run. This usually means"
echo "*** that the run-time linker is not finding libOggFLAC or finding the wrong"
echo "*** version of libOggFLAC. If it is not finding libOggFLAC, you'll need to set your"
echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point"
echo "*** to the installed location Also, make sure you have run ldconfig if that"
echo "*** is required on your system"
echo "***"
echo "*** If you have an old version installed, it is best to remove it, although"
echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH"],
[ echo "*** The test program failed to compile or link. See the file config.log for the"
echo "*** exact error that occured. This usually means libOggFLAC was incorrectly installed"
echo "*** or that you have moved libOggFLAC since it was installed. In the latter case, you"
echo "*** may want to edit the libOggFLAC-config script: $LIBOGGFLAC_CONFIG" ])
CFLAGS="$ac_save_CFLAGS"
LIBS="$ac_save_LIBS"
fi
LIBOGGFLAC_CFLAGS=""
LIBOGGFLAC_LIBS=""
ifelse([$2], , :, [$2])
fi
AC_SUBST(LIBOGGFLAC_CFLAGS)
AC_SUBST(LIBOGGFLAC_LIBS)
rm -f conf.libOggFLACtest
])

14
m4/libwrap.m4 Normal file
View File

@@ -0,0 +1,14 @@
dnl
dnl Usage:
dnl AC_CHECK_LIBWRAP([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
dnl
AC_DEFUN([AC_CHECK_LIBWRAP],[
AC_CHECK_HEADERS([tcpd.h],
AC_CHECK_LIB([wrap], [request_init],
[LIBWRAP_CFLAGS=""
LIBWRAP_LDFLAGS="-lwrap"
$1],
$2),
$2)
])

19
m4/pretty_print.m4 Normal file
View File

@@ -0,0 +1,19 @@
AC_DEFUN([results], [
dnl This is a hack to allow "with" names, otherwise "enable".
num=`expr match $1 'with'`
if test "$num" != "0"; then
var="`echo '$'$1`"
else
var="`echo '$'enable_$1`"
fi
echo -n '('
if eval "test x$var = xyes"; then
echo -n '+'
elif test -n "$3" && eval "test x$var = x$3"; then
echo -n '+'
else
echo -n '-'
fi
echo -n "$2) "
])

47
scripts/check_config_h.rb Executable file
View File

@@ -0,0 +1,47 @@
#!/usr/bin/env ruby
#
# This script verifies that every source includes config.h first.
# This is very important for consistent Large File Support.
#
def check_file(file)
first = true
file.each_line do |line|
if line =~ /^\#include\s+(\S+)/ then
if $1 == '"config.h"'
unless first
puts "#{file.path}: config.h included too late"
end
else
if first
puts "#{file.path}: config.h missing"
end
end
first = false
end
end
end
def check_path(path)
File.open(path) do |file|
check_file(file)
end
end
if ARGV.empty?
Dir["src/*.c"].each do |path|
check_path(path)
end
Dir["src/*/*.c"].each do |path|
check_path(path)
end
Dir["test/*.c"].each do |path|
check_path(path)
end
else
ARGV.each do |path|
check_path(path)
end
end

View File

@@ -18,6 +18,7 @@ test -x configure || NOCONFIGURE=1 ./autogen.sh
./configure --prefix=$PREFIX/full \
--disable-dependency-tracking --enable-debug --enable-werror \
--enable-un \
--enable-modplug \
--enable-ao --enable-mikmod --enable-mvp
$MAKE install
$MAKE distclean
@@ -47,6 +48,7 @@ $MAKE install
$MAKE distclean
# shout: ogg without mp3
# sndfile instead of modplug
./configure --prefix=$PREFIX/shout_ogg \
--disable-dependency-tracking --disable-debug --enable-werror \
--disable-tcp \
@@ -56,6 +58,7 @@ $MAKE distclean
--enable-shout-ogg --disable-shout-mp3 --disable-lame-encoder \
--disable-ffmpeg --disable-wavpack --disable-mpc --disable-aac \
--disable-flac --enable-vorbis --disable-oggflac --disable-audiofile \
--disable-modplug --enable-sndfile \
--with-zeroconf=no
$MAKE install
$MAKE distclean

View File

@@ -0,0 +1,185 @@
/* compress.c
* Compressor logic
*
* (c)2007 busybee (http://beesbuzz.biz/
* Licensed under the terms of the LGPL. See the file COPYING for details.
*/
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "config.h"
#include "compress.h"
struct Compressor {
//! The compressor's preferences
struct CompressorConfig prefs;
//! History of the peak values
int *peaks;
//! History of the gain values
int *gain;
//! History of clip amounts
int *clipped;
unsigned int pos;
unsigned int bufsz;
};
struct Compressor *Compressor_new(unsigned int history)
{
struct Compressor *obj = malloc(sizeof(struct Compressor));
obj->prefs.target = TARGET;
obj->prefs.maxgain = GAINMAX;
obj->prefs.smooth = GAINSMOOTH;
obj->peaks = obj->gain = obj->clipped = NULL;
obj->bufsz = 0;
obj->pos = 0;
Compressor_setHistory(obj, history);
return obj;
}
void Compressor_delete(struct Compressor *obj)
{
if (obj->peaks)
free(obj->peaks);
if (obj->gain)
free(obj->gain);
if (obj->clipped)
free(obj->clipped);
free(obj);
}
static int *resizeArray(int *data, int newsz, int oldsz)
{
data = realloc(data, newsz*sizeof(int));
if (newsz > oldsz)
memset(data + oldsz, 0, sizeof(int)*(newsz - oldsz));
return data;
}
void Compressor_setHistory(struct Compressor *obj, unsigned int history)
{
if (!history)
history = BUCKETS;
obj->peaks = resizeArray(obj->peaks, history, obj->bufsz);
obj->gain = resizeArray(obj->gain, history, obj->bufsz);
obj->clipped = resizeArray(obj->clipped, history, obj->bufsz);
obj->bufsz = history;
}
struct CompressorConfig *Compressor_getConfig(struct Compressor *obj)
{
return &obj->prefs;
}
void Compressor_Process_int16(struct Compressor *obj, int16_t *audio,
unsigned int count)
{
struct CompressorConfig *prefs = Compressor_getConfig(obj);
int16_t *ap;
unsigned int i;
int *peaks = obj->peaks;
int curGain = obj->gain[obj->pos];
int newGain;
int peakVal = 1;
int peakPos = 0;
int slot = (obj->pos + 1) % obj->bufsz;
int *clipped = obj->clipped + slot;
unsigned int ramp = count;
int delta;
ap = audio;
for (i = 0; i < count; i++)
{
int val = *ap++;
if (val < 0)
val = -val;
if (val > peakVal)
{
peakVal = val;
peakPos = i;
}
}
peaks[slot] = peakVal;
for (i = 0; i < obj->bufsz; i++)
{
if (peaks[i] > peakVal)
{
peakVal = peaks[i];
peakPos = 0;
}
}
//! Determine target gain
newGain = (1 << 10)*prefs->target/peakVal;
//! Adjust the gain with inertia from the previous gain value
newGain = (curGain*((1 << prefs->smooth) - 1) + newGain)
>> prefs->smooth;
//! Make sure it's no more than the maximum gain value
if (newGain > (prefs->maxgain << 10))
newGain = prefs->maxgain << 10;
//! Make sure it's no less than 1:1
if (newGain < (1 << 10))
newGain = 1 << 10;
//! Make sure the adjusted gain won't cause clipping
if ((peakVal*newGain >> 10) > 32767)
{
newGain = (32767 << 10)/peakVal;
//! Truncate the ramp time
ramp = peakPos;
}
//! Record the new gain
obj->gain[slot] = newGain;
if (!ramp)
ramp = 1;
if (!curGain)
curGain = 1 << 10;
delta = (newGain - curGain) / (int)ramp;
ap = audio;
*clipped = 0;
for (i = 0; i < count; i++)
{
int sample;
//! Amplify the sample
sample = *ap*curGain >> 10;
if (sample < -32768)
{
*clipped += -32768 - sample;
sample = -32768;
} else if (sample > 32767)
{
*clipped += sample - 32767;
sample = 32767;
}
*ap++ = sample;
//! Adjust the gain
if (i < ramp)
curGain += delta;
else
curGain = newGain;
}
obj->pos = slot;
}

View File

@@ -0,0 +1,40 @@
/*! compress.h
* interface to audio compression
*
* (c)2007 busybee (http://beesbuzz.biz/)
* Licensed under the terms of the LGPL. See the file COPYING for details.
*/
#ifndef COMPRESS_H
#define COMPRESS_H
#include <stdint.h>
//! Configuration values for the compressor object
struct CompressorConfig {
int target;
int maxgain;
int smooth;
};
struct Compressor;
//! Create a new compressor (use history value of 0 for default)
struct Compressor *Compressor_new(unsigned int history);
//! Delete a compressor
void Compressor_delete(struct Compressor *);
//! Set the history length
void Compressor_setHistory(struct Compressor *, unsigned int history);
//! Get the configuration for a compressor
struct CompressorConfig *Compressor_getConfig(struct Compressor *);
//! Process 16-bit signed data
void Compressor_Process_int16(struct Compressor *, int16_t *data, unsigned int count);
//! TODO: Compressor_Process_int32, Compressor_Process_float, others as needed
//! TODO: functions for getting at the peak/gain/clip history buffers (for monitoring)
#endif

View File

@@ -0,0 +1,19 @@
/* config.h
** Default values for the configuration, and also a few random debug things
*/
#ifndef AC_CONFIG_H
#define AC_CONFIG_H
/*** Version information ***/
#define ACVERSION "2.0"
/*** Default configuration stuff ***/
#define TARGET 16384 /*!< Target level (on a scale of 0-32767) */
#define GAINMAX 32 /*!< The maximum amount to amplify by */
#define GAINSMOOTH 8 /*!< How much inertia ramping has*/
#define BUCKETS 400 /*!< How long of a history to use by default */
#endif

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2003-2009 The Music Player Daemon Project
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2003-2009 The Music Player Daemon Project
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,6 +17,7 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h" /* must be first for large file support */
#include "aiff.h"
#include <glib.h>

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2003-2009 The Music Player Daemon Project
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify

View File

@@ -0,0 +1,301 @@
/*
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program 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.
*
* This program 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 this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/**
* single bz2 archive handling (requires libbz2)
*/
#include "config.h"
#include "archive/bz2_archive_plugin.h"
#include "archive_api.h"
#include "input_plugin.h"
#include "refcount.h"
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include <glib.h>
#include <bzlib.h>
#ifdef HAVE_OLDER_BZIP2
#define BZ2_bzDecompressInit bzDecompressInit
#define BZ2_bzDecompress bzDecompress
#endif
struct bz2_archive_file {
struct archive_file base;
struct refcount ref;
char *name;
bool reset;
struct input_stream *istream;
};
struct bz2_input_stream {
struct input_stream base;
struct bz2_archive_file *archive;
bool eof;
bz_stream bzstream;
char buffer[5000];
};
static const struct input_plugin bz2_inputplugin;
static inline GQuark
bz2_quark(void)
{
return g_quark_from_static_string("bz2");
}
/* single archive handling allocation helpers */
static bool
bz2_alloc(struct bz2_input_stream *data, GError **error_r)
{
int ret;
data->bzstream.bzalloc = NULL;
data->bzstream.bzfree = NULL;
data->bzstream.opaque = NULL;
data->bzstream.next_in = (void *) data->buffer;
data->bzstream.avail_in = 0;
ret = BZ2_bzDecompressInit(&data->bzstream, 0, 0);
if (ret != BZ_OK) {
g_free(data);
g_set_error(error_r, bz2_quark(), ret,
"BZ2_bzDecompressInit() has failed");
return false;
}
return true;
}
static void
bz2_destroy(struct bz2_input_stream *data)
{
BZ2_bzDecompressEnd(&data->bzstream);
}
/* archive open && listing routine */
static struct archive_file *
bz2_open(const char *pathname, GError **error_r)
{
struct bz2_archive_file *context;
int len;
context = g_malloc(sizeof(*context));
archive_file_init(&context->base, &bz2_archive_plugin);
refcount_init(&context->ref);
//open archive
context->istream = input_stream_open(pathname, error_r);
if (context->istream == NULL) {
g_free(context);
return NULL;
}
context->name = g_path_get_basename(pathname);
//remove suffix
len = strlen(context->name);
if (len > 4) {
context->name[len - 4] = 0; //remove .bz2 suffix
}
return &context->base;
}
static void
bz2_scan_reset(struct archive_file *file)
{
struct bz2_archive_file *context = (struct bz2_archive_file *) file;
context->reset = true;
}
static char *
bz2_scan_next(struct archive_file *file)
{
struct bz2_archive_file *context = (struct bz2_archive_file *) file;
char *name = NULL;
if (context->reset) {
name = context->name;
context->reset = false;
}
return name;
}
static void
bz2_close(struct archive_file *file)
{
struct bz2_archive_file *context = (struct bz2_archive_file *) file;
if (!refcount_dec(&context->ref))
return;
g_free(context->name);
input_stream_close(context->istream);
g_free(context);
}
/* single archive handling */
static struct input_stream *
bz2_open_stream(struct archive_file *file, const char *path, GError **error_r)
{
struct bz2_archive_file *context = (struct bz2_archive_file *) file;
struct bz2_input_stream *bis = g_new(struct bz2_input_stream, 1);
input_stream_init(&bis->base, &bz2_inputplugin, path);
bis->archive = context;
bis->base.ready = true;
bis->base.seekable = false;
if (!bz2_alloc(bis, error_r)) {
input_stream_deinit(&bis->base);
g_free(bis);
return NULL;
}
bis->eof = false;
refcount_inc(&context->ref);
return &bis->base;
}
static void
bz2_is_close(struct input_stream *is)
{
struct bz2_input_stream *bis = (struct bz2_input_stream *)is;
bz2_destroy(bis);
bz2_close(&bis->archive->base);
input_stream_deinit(&bis->base);
g_free(bis);
}
static bool
bz2_fillbuffer(struct bz2_input_stream *bis, GError **error_r)
{
size_t count;
bz_stream *bzstream;
bzstream = &bis->bzstream;
if (bzstream->avail_in > 0)
return true;
count = input_stream_read(bis->archive->istream,
bis->buffer, sizeof(bis->buffer),
error_r);
if (count == 0)
return false;
bzstream->next_in = bis->buffer;
bzstream->avail_in = count;
return true;
}
static size_t
bz2_is_read(struct input_stream *is, void *ptr, size_t length,
GError **error_r)
{
struct bz2_input_stream *bis = (struct bz2_input_stream *)is;
bz_stream *bzstream;
int bz_result;
size_t nbytes = 0;
if (bis->eof)
return 0;
bzstream = &bis->bzstream;
bzstream->next_out = ptr;
bzstream->avail_out = length;
do {
if (!bz2_fillbuffer(bis, error_r))
return 0;
bz_result = BZ2_bzDecompress(bzstream);
if (bz_result == BZ_STREAM_END) {
bis->eof = true;
break;
}
if (bz_result != BZ_OK) {
g_set_error(error_r, bz2_quark(), bz_result,
"BZ2_bzDecompress() has failed");
return 0;
}
} while (bzstream->avail_out == length);
nbytes = length - bzstream->avail_out;
is->offset += nbytes;
return nbytes;
}
static bool
bz2_is_eof(struct input_stream *is)
{
struct bz2_input_stream *bis = (struct bz2_input_stream *)is;
return bis->eof;
}
/* exported structures */
static const char *const bz2_extensions[] = {
"bz2",
NULL
};
static const struct input_plugin bz2_inputplugin = {
.close = bz2_is_close,
.read = bz2_is_read,
.eof = bz2_is_eof,
};
const struct archive_plugin bz2_archive_plugin = {
.name = "bz2",
.open = bz2_open,
.scan_reset = bz2_scan_reset,
.scan_next = bz2_scan_next,
.open_stream = bz2_open_stream,
.close = bz2_close,
.suffixes = bz2_extensions
};

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2003-2009 The Music Player Daemon Project
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,9 +17,9 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef LASTFM_INPUT_PLUGIN_H
#define LASTFM_INPUT_PLUGIN_H
#ifndef MPD_ARCHIVE_BZ2_H
#define MPD_ARCHIVE_BZ2_H
extern const struct input_plugin lastfm_input_plugin;
extern const struct archive_plugin bz2_archive_plugin;
#endif

View File

@@ -1,284 +0,0 @@
/*
* Copyright (C) 2003-2009 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program 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.
*
* This program 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 this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/**
* single bz2 archive handling (requires libbz2)
*/
#include "archive_api.h"
#include "input_plugin.h"
#include "config.h"
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include <glib.h>
#include <bzlib.h>
#ifdef HAVE_OLDER_BZIP2
#define BZ2_bzDecompressInit bzDecompressInit
#define BZ2_bzDecompress bzDecompress
#endif
#define BZ_BUFSIZE 5000
typedef struct {
char *name;
bool reset;
struct input_stream istream;
int last_bz_result;
int last_parent_result;
bz_stream bzstream;
char *buffer;
} bz2_context;
static const struct input_plugin bz2_inputplugin;
/* single archive handling allocation helpers */
static bool
bz2_alloc(bz2_context *data)
{
data->bzstream.bzalloc = NULL;
data->bzstream.bzfree = NULL;
data->bzstream.opaque = NULL;
data->buffer = g_malloc(BZ_BUFSIZE);
data->bzstream.next_in = (void *) data->buffer;
data->bzstream.avail_in = 0;
if (BZ2_bzDecompressInit(&data->bzstream, 0, 0) != BZ_OK) {
g_free(data->buffer);
g_free(data);
return false;
}
data->last_bz_result = BZ_OK;
data->last_parent_result = 0;
return true;
}
static void
bz2_destroy(bz2_context *data)
{
BZ2_bzDecompressEnd(&data->bzstream);
g_free(data->buffer);
}
/* archive open && listing routine */
static struct archive_file *
bz2_open(char * pathname)
{
bz2_context *context;
char *name;
int len;
context = g_malloc(sizeof(bz2_context));
if (!context) {
return NULL;
}
//open archive
if (!input_stream_open(&context->istream, pathname)) {
g_warning("failed to open an bzip2 archive %s\n",pathname);
g_free(context);
return NULL;
}
//capture filename
name = strrchr(pathname, '/');
if (name == NULL) {
g_warning("failed to get bzip2 name from %s\n",pathname);
g_free(context);
return NULL;
}
context->name = g_strdup(name+1);
//remove suffix
len = strlen(context->name);
if (len > 4) {
context->name[len-4] = 0; //remove .bz2 suffix
}
return (struct archive_file *) context;
}
static void
bz2_scan_reset(struct archive_file *file)
{
bz2_context *context = (bz2_context *) file;
context->reset = true;
}
static char *
bz2_scan_next(struct archive_file *file)
{
bz2_context *context = (bz2_context *) file;
char *name = NULL;
if (context->reset) {
name = context->name;
context->reset = false;
}
return name;
}
static void
bz2_close(struct archive_file *file)
{
bz2_context *context = (bz2_context *) file;
g_free(context->name);
input_stream_close(&context->istream);
g_free(context);
}
/* single archive handling */
static bool
bz2_open_stream(struct archive_file *file, struct input_stream *is,
G_GNUC_UNUSED const char *path)
{
bz2_context *context = (bz2_context *) file;
//setup file ops
is->plugin = &bz2_inputplugin;
//insert back reference
is->data = context;
is->seekable = false;
if (!bz2_alloc(context)) {
g_warning("alloc bz2 failed\n");
return false;
}
return true;
}
static void
bz2_is_close(struct input_stream *is)
{
bz2_context *context = (bz2_context *) is->data;
bz2_destroy(context);
is->data = NULL;
bz2_close((struct archive_file *)context);
}
static int
bz2_fillbuffer(bz2_context *context,
size_t numBytes)
{
size_t count;
bz_stream *bzstream;
bzstream = &context->bzstream;
if (bzstream->avail_in > 0)
return 0;
count = input_stream_read(&context->istream,
context->buffer, BZ_BUFSIZE);
if (count == 0) {
if (bzstream->avail_out == numBytes)
return -1;
if (!input_stream_eof(&context->istream))
context->last_parent_result = 1;
} else {
bzstream->next_in = context->buffer;
bzstream->avail_in = count;
}
return 0;
}
static size_t
bz2_is_read(struct input_stream *is, void *ptr, size_t size)
{
bz2_context *context = (bz2_context *) is->data;
bz_stream *bzstream;
int bz_result;
size_t numBytes = size;
size_t bytesRead = 0;
if (context->last_bz_result != BZ_OK)
return 0;
if (context->last_parent_result != 0)
return 0;
bzstream = &context->bzstream;
bzstream->next_out = ptr;
bzstream->avail_out = numBytes;
while (bzstream->avail_out != 0) {
if (bz2_fillbuffer(context, numBytes) != 0)
break;
bz_result = BZ2_bzDecompress(bzstream);
if (context->last_bz_result != BZ_OK
&& bzstream->avail_out == numBytes) {
context->last_bz_result = bz_result;
break;
}
if (bz_result == BZ_STREAM_END) {
context->last_bz_result = bz_result;
break;
}
}
bytesRead = numBytes - bzstream->avail_out;
is->offset += bytesRead;
return bytesRead;
}
static bool
bz2_is_eof(struct input_stream *is)
{
bz2_context *context = (bz2_context *) is->data;
if (context->last_bz_result == BZ_STREAM_END) {
return true;
}
return false;
}
/* exported structures */
static const char *const bz2_extensions[] = {
"bz2",
NULL
};
static const struct input_plugin bz2_inputplugin = {
.close = bz2_is_close,
.read = bz2_is_read,
.eof = bz2_is_eof,
};
const struct archive_plugin bz2_plugin = {
.name = "bz2",
.open = bz2_open,
.scan_reset = bz2_scan_reset,
.scan_next = bz2_scan_next,
.open_stream = bz2_open_stream,
.close = bz2_close,
.suffixes = bz2_extensions
};

View File

@@ -0,0 +1,287 @@
/*
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program 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.
*
* This program 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 this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/**
* iso archive handling (requires cdio, and iso9660)
*/
#include "config.h"
#include "archive/iso9660_archive_plugin.h"
#include "archive_api.h"
#include "input_plugin.h"
#include "refcount.h"
#include <cdio/cdio.h>
#include <cdio/iso9660.h>
#include <glib.h>
#include <string.h>
#define CEILING(x, y) ((x+(y-1))/y)
struct iso9660_archive_file {
struct archive_file base;
struct refcount ref;
iso9660_t *iso;
GSList *list;
GSList *iter;
};
static const struct input_plugin iso9660_input_plugin;
static inline GQuark
iso9660_quark(void)
{
return g_quark_from_static_string("iso9660");
}
/* archive open && listing routine */
static void
listdir_recur(const char *psz_path, struct iso9660_archive_file *context)
{
iso9660_t *iso = context->iso;
CdioList_t *entlist;
CdioListNode_t *entnode;
iso9660_stat_t *statbuf;
char pathname[4096];
entlist = iso9660_ifs_readdir (iso, psz_path);
if (!entlist) {
return;
}
/* Iterate over the list of nodes that iso9660_ifs_readdir gives */
_CDIO_LIST_FOREACH (entnode, entlist) {
statbuf = (iso9660_stat_t *) _cdio_list_node_data (entnode);
strcpy(pathname, psz_path);
strcat(pathname, statbuf->filename);
if (_STAT_DIR == statbuf->type ) {
if (strcmp(statbuf->filename, ".") && strcmp(statbuf->filename, "..")) {
strcat(pathname, "/");
listdir_recur(pathname, context);
}
} else {
//remove leading /
context->list = g_slist_prepend( context->list,
g_strdup(pathname + 1));
}
}
_cdio_list_free (entlist, true);
}
static struct archive_file *
iso9660_archive_open(const char *pathname, GError **error_r)
{
struct iso9660_archive_file *context =
g_new(struct iso9660_archive_file, 1);
archive_file_init(&context->base, &iso9660_archive_plugin);
refcount_init(&context->ref);
context->list = NULL;
/* open archive */
context->iso = iso9660_open (pathname);
if (context->iso == NULL) {
g_set_error(error_r, iso9660_quark(), 0,
"Failed to open ISO9660 file %s", pathname);
return NULL;
}
listdir_recur("/", context);
return &context->base;
}
static void
iso9660_archive_scan_reset(struct archive_file *file)
{
struct iso9660_archive_file *context =
(struct iso9660_archive_file *)file;
//reset iterator
context->iter = context->list;
}
static char *
iso9660_archive_scan_next(struct archive_file *file)
{
struct iso9660_archive_file *context =
(struct iso9660_archive_file *)file;
char *data = NULL;
if (context->iter != NULL) {
///fetch data and goto next
data = context->iter->data;
context->iter = g_slist_next(context->iter);
}
return data;
}
static void
iso9660_archive_close(struct archive_file *file)
{
struct iso9660_archive_file *context =
(struct iso9660_archive_file *)file;
GSList *tmp;
if (!refcount_dec(&context->ref))
return;
if (context->list) {
//free list
for (tmp = context->list; tmp != NULL; tmp = g_slist_next(tmp))
g_free(tmp->data);
g_slist_free(context->list);
}
//close archive
iso9660_close(context->iso);
g_free(context);
}
/* single archive handling */
struct iso9660_input_stream {
struct input_stream base;
struct iso9660_archive_file *archive;
iso9660_stat_t *statbuf;
size_t max_blocks;
};
static struct input_stream *
iso9660_archive_open_stream(struct archive_file *file,
const char *pathname, GError **error_r)
{
struct iso9660_archive_file *context =
(struct iso9660_archive_file *)file;
struct iso9660_input_stream *iis;
iis = g_new(struct iso9660_input_stream, 1);
input_stream_init(&iis->base, &iso9660_input_plugin, pathname);
iis->archive = context;
iis->statbuf = iso9660_ifs_stat_translate(context->iso, pathname);
if (iis->statbuf == NULL) {
g_free(iis);
g_set_error(error_r, iso9660_quark(), 0,
"not found in the ISO file: %s", pathname);
return NULL;
}
iis->base.ready = true;
//we are not seekable
iis->base.seekable = false;
iis->base.size = iis->statbuf->size;
iis->max_blocks = CEILING(iis->statbuf->size, ISO_BLOCKSIZE);
refcount_inc(&context->ref);
return &iis->base;
}
static void
iso9660_input_close(struct input_stream *is)
{
struct iso9660_input_stream *iis = (struct iso9660_input_stream *)is;
g_free(iis->statbuf);
iso9660_archive_close(&iis->archive->base);
input_stream_deinit(&iis->base);
g_free(iis);
}
static size_t
iso9660_input_read(struct input_stream *is, void *ptr, size_t size, GError **error_r)
{
struct iso9660_input_stream *iis = (struct iso9660_input_stream *)is;
int toread, readed = 0;
int no_blocks, cur_block;
size_t left_bytes = iis->statbuf->size - is->offset;
size = (size * ISO_BLOCKSIZE) / ISO_BLOCKSIZE;
if (left_bytes < size) {
toread = left_bytes;
no_blocks = CEILING(left_bytes,ISO_BLOCKSIZE);
} else {
toread = size;
no_blocks = toread / ISO_BLOCKSIZE;
}
if (no_blocks > 0) {
cur_block = is->offset / ISO_BLOCKSIZE;
readed = iso9660_iso_seek_read (iis->archive->iso, ptr,
iis->statbuf->lsn + cur_block, no_blocks);
if (readed != no_blocks * ISO_BLOCKSIZE) {
g_set_error(error_r, iso9660_quark(), 0,
"error reading ISO file at lsn %lu",
(long unsigned int) cur_block);
return 0;
}
if (left_bytes < size) {
readed = left_bytes;
}
is->offset += readed;
}
return readed;
}
static bool
iso9660_input_eof(struct input_stream *is)
{
return is->offset == is->size;
}
/* exported structures */
static const char *const iso9660_archive_extensions[] = {
"iso",
NULL
};
static const struct input_plugin iso9660_input_plugin = {
.close = iso9660_input_close,
.read = iso9660_input_read,
.eof = iso9660_input_eof,
};
const struct archive_plugin iso9660_archive_plugin = {
.name = "iso",
.open = iso9660_archive_open,
.scan_reset = iso9660_archive_scan_reset,
.scan_next = iso9660_archive_scan_next,
.open_stream = iso9660_archive_open_stream,
.close = iso9660_archive_close,
.suffixes = iso9660_archive_extensions
};

View File

@@ -0,0 +1,25 @@
/*
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program 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.
*
* This program 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 this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef MPD_ARCHIVE_ISO9660_H
#define MPD_ARCHIVE_ISO9660_H
extern const struct archive_plugin iso9660_archive_plugin;
#endif

View File

@@ -1,239 +0,0 @@
/*
* Copyright (C) 2003-2009 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program 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.
*
* This program 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 this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/**
* iso archive handling (requires cdio, and iso9660)
*/
#include "archive_api.h"
#include "input_plugin.h"
#include <cdio/cdio.h>
#include <cdio/iso9660.h>
#include <glib.h>
#include <string.h>
#define CEILING(x, y) ((x+(y-1))/y)
typedef struct {
iso9660_t *iso;
iso9660_stat_t *statbuf;
size_t cur_ofs;
size_t max_blocks;
GSList *list;
GSList *iter;
} iso_context;
static const struct input_plugin iso_inputplugin;
/* archive open && listing routine */
static void
listdir_recur(const char *psz_path, iso_context *context)
{
iso9660_t *iso = context->iso;
CdioList_t *entlist;
CdioListNode_t *entnode;
iso9660_stat_t *statbuf;
char pathname[4096];
entlist = iso9660_ifs_readdir (iso, psz_path);
if (!entlist) {
return;
}
/* Iterate over the list of nodes that iso9660_ifs_readdir gives */
_CDIO_LIST_FOREACH (entnode, entlist) {
statbuf = (iso9660_stat_t *) _cdio_list_node_data (entnode);
strcpy(pathname, psz_path);
strcat(pathname, statbuf->filename);
if (_STAT_DIR == statbuf->type ) {
if (strcmp(statbuf->filename, ".") && strcmp(statbuf->filename, "..")) {
strcat(pathname, "/");
listdir_recur(pathname, context);
}
} else {
//remove leading /
context->list = g_slist_prepend( context->list,
g_strdup(pathname + 1));
}
}
_cdio_list_free (entlist, true);
}
static struct archive_file *
iso_open(char * pathname)
{
iso_context *context = g_malloc(sizeof(iso_context));
context->list = NULL;
/* open archive */
context->iso = iso9660_open (pathname);
if (context->iso == NULL) {
g_warning("iso %s open failed\n", pathname);
return NULL;
}
listdir_recur("/", context);
return (struct archive_file *)context;
}
static void
iso_scan_reset(struct archive_file *file)
{
iso_context *context = (iso_context *) file;
//reset iterator
context->iter = context->list;
}
static char *
iso_scan_next(struct archive_file *file)
{
iso_context *context = (iso_context *) file;
char *data = NULL;
if (context->iter != NULL) {
///fetch data and goto next
data = context->iter->data;
context->iter = g_slist_next(context->iter);
}
return data;
}
static void
iso_close(struct archive_file *file)
{
iso_context *context = (iso_context *) file;
GSList *tmp;
if (context->list) {
//free list
for (tmp = context->list; tmp != NULL; tmp = g_slist_next(tmp))
g_free(tmp->data);
g_slist_free(context->list);
}
//close archive
iso9660_close(context->iso);
g_free(context);
}
/* single archive handling */
static bool
iso_open_stream(struct archive_file *file, struct input_stream *is,
const char *pathname)
{
iso_context *context = (iso_context *) file;
//setup file ops
is->plugin = &iso_inputplugin;
//insert back reference
is->data = context;
//we are not seekable
is->seekable = false;
context->statbuf = iso9660_ifs_stat_translate (context->iso, pathname);
if (context->statbuf == NULL) {
g_warning("file %s not found in iso\n", pathname);
return false;
}
context->cur_ofs = 0;
context->max_blocks = CEILING(context->statbuf->size, ISO_BLOCKSIZE);
return true;
}
static void
iso_is_close(struct input_stream *is)
{
iso_context *context = (iso_context *) is->data;
g_free(context->statbuf);
iso_close((struct archive_file *)context);
}
static size_t
iso_is_read(struct input_stream *is, void *ptr, size_t size)
{
iso_context *context = (iso_context *) is->data;
int toread, readed = 0;
int no_blocks, cur_block;
size_t left_bytes = context->statbuf->size - context->cur_ofs;
size = (size * ISO_BLOCKSIZE) / ISO_BLOCKSIZE;
if (left_bytes < size) {
toread = left_bytes;
no_blocks = CEILING(left_bytes,ISO_BLOCKSIZE);
} else {
toread = size;
no_blocks = toread / ISO_BLOCKSIZE;
}
if (no_blocks > 0) {
cur_block = context->cur_ofs / ISO_BLOCKSIZE;
readed = iso9660_iso_seek_read (context->iso, ptr,
context->statbuf->lsn + cur_block, no_blocks);
if (readed != no_blocks * ISO_BLOCKSIZE) {
g_warning("error reading ISO file at lsn %lu\n",
(long unsigned int) cur_block );
return -1;
}
if (left_bytes < size) {
readed = left_bytes;
}
context->cur_ofs += readed;
}
return readed;
}
static bool
iso_is_eof(struct input_stream *is)
{
iso_context *context = (iso_context *) is->data;
return (context->cur_ofs == context->statbuf->size);
}
/* exported structures */
static const char *const iso_extensions[] = {
"iso",
NULL
};
static const struct input_plugin iso_inputplugin = {
.close = iso_is_close,
.read = iso_is_read,
.eof = iso_is_eof,
};
const struct archive_plugin iso_plugin = {
.name = "iso",
.open = iso_open,
.scan_reset = iso_scan_reset,
.scan_next = iso_scan_next,
.open_stream = iso_open_stream,
.close = iso_close,
.suffixes = iso_extensions
};

View File

@@ -1,196 +0,0 @@
/*
* Copyright (C) 2003-2009 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program 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.
*
* This program 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 this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/**
* zip archive handling (requires zziplib)
*/
#include "archive_api.h"
#include "archive_api.h"
#include "input_plugin.h"
#include <zzip/zzip.h>
#include <glib.h>
#include <string.h>
typedef struct {
ZZIP_DIR *dir;
ZZIP_FILE *file;
size_t length;
GSList *list;
GSList *iter;
} zip_context;
static const struct input_plugin zip_inputplugin;
/* archive open && listing routine */
static struct archive_file *
zip_open(char * pathname)
{
zip_context *context = g_malloc(sizeof(zip_context));
ZZIP_DIRENT dirent;
// open archive
context->list = NULL;
context->dir = zzip_dir_open(pathname, NULL);
if (context->dir == NULL) {
g_warning("zipfile %s open failed\n", pathname);
return NULL;
}
while (zzip_dir_read(context->dir, &dirent)) {
//add only files
if (dirent.st_size > 0) {
context->list = g_slist_prepend(context->list,
g_strdup(dirent.d_name));
}
}
return (struct archive_file *)context;
}
static void
zip_scan_reset(struct archive_file *file)
{
zip_context *context = (zip_context *) file;
//reset iterator
context->iter = context->list;
}
static char *
zip_scan_next(struct archive_file *file)
{
zip_context *context = (zip_context *) file;
char *data = NULL;
if (context->iter != NULL) {
///fetch data and goto next
data = context->iter->data;
context->iter = g_slist_next(context->iter);
}
return data;
}
static void
zip_close(struct archive_file *file)
{
zip_context *context = (zip_context *) file;
if (context->list) {
//free list
for (GSList *tmp = context->list; tmp != NULL; tmp = g_slist_next(tmp))
g_free(tmp->data);
g_slist_free(context->list);
}
//close archive
zzip_dir_close (context->dir);
g_free(context);
}
/* single archive handling */
static bool
zip_open_stream(struct archive_file *file, struct input_stream *is,
const char *pathname)
{
zip_context *context = (zip_context *) file;
ZZIP_STAT z_stat;
//setup file ops
is->plugin = &zip_inputplugin;
//insert back reference
is->data = context;
//we are seekable (but its not recommendent to do so)
is->seekable = true;
context->file = zzip_file_open(context->dir, pathname, 0);
if (!context->file) {
g_warning("file %s not found in the zipfile\n", pathname);
return false;
}
zzip_file_stat(context->file, &z_stat);
context->length = z_stat.st_size;
return true;
}
static void
zip_is_close(struct input_stream *is)
{
zip_context *context = (zip_context *) is->data;
zzip_file_close (context->file);
zip_close((struct archive_file *)context);
}
static size_t
zip_is_read(struct input_stream *is, void *ptr, size_t size)
{
zip_context *context = (zip_context *) is->data;
int ret;
ret = zzip_file_read(context->file, ptr, size);
if (ret < 0) {
g_warning("error %d reading zipfile\n", ret);
return 0;
}
return ret;
}
static bool
zip_is_eof(struct input_stream *is)
{
zip_context *context = (zip_context *) is->data;
return ((size_t) zzip_tell(context->file) == context->length);
}
static bool
zip_is_seek(G_GNUC_UNUSED struct input_stream *is,
G_GNUC_UNUSED off_t offset, G_GNUC_UNUSED int whence)
{
zip_context *context = (zip_context *) is->data;
zzip_off_t ofs = zzip_seek(context->file, offset, whence);
if (ofs != -1) {
is->offset = ofs;
return true;
}
return false;
}
/* exported structures */
static const char *const zip_extensions[] = {
"zip",
NULL
};
static const struct input_plugin zip_inputplugin = {
.close = zip_is_close,
.read = zip_is_read,
.eof = zip_is_eof,
.seek = zip_is_seek,
};
const struct archive_plugin zip_plugin = {
.name = "zip",
.open = zip_open,
.scan_reset = zip_scan_reset,
.scan_next = zip_scan_next,
.open_stream = zip_open_stream,
.close = zip_close,
.suffixes = zip_extensions
};

View File

@@ -0,0 +1,242 @@
/*
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program 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.
*
* This program 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 this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/**
* zip archive handling (requires zziplib)
*/
#include "config.h"
#include "archive/zzip_archive_plugin.h"
#include "archive_api.h"
#include "archive_api.h"
#include "input_plugin.h"
#include "refcount.h"
#include <zzip/zzip.h>
#include <glib.h>
#include <string.h>
struct zzip_archive {
struct archive_file base;
struct refcount ref;
ZZIP_DIR *dir;
GSList *list;
GSList *iter;
};
static const struct input_plugin zzip_input_plugin;
static inline GQuark
zzip_quark(void)
{
return g_quark_from_static_string("zzip");
}
/* archive open && listing routine */
static struct archive_file *
zzip_archive_open(const char *pathname, GError **error_r)
{
struct zzip_archive *context = g_malloc(sizeof(*context));
ZZIP_DIRENT dirent;
archive_file_init(&context->base, &zzip_archive_plugin);
refcount_init(&context->ref);
// open archive
context->list = NULL;
context->dir = zzip_dir_open(pathname, NULL);
if (context->dir == NULL) {
g_set_error(error_r, zzip_quark(), 0,
"Failed to open ZIP file %s", pathname);
return NULL;
}
while (zzip_dir_read(context->dir, &dirent)) {
//add only files
if (dirent.st_size > 0) {
context->list = g_slist_prepend(context->list,
g_strdup(dirent.d_name));
}
}
return &context->base;
}
static void
zzip_archive_scan_reset(struct archive_file *file)
{
struct zzip_archive *context = (struct zzip_archive *) file;
//reset iterator
context->iter = context->list;
}
static char *
zzip_archive_scan_next(struct archive_file *file)
{
struct zzip_archive *context = (struct zzip_archive *) file;
char *data = NULL;
if (context->iter != NULL) {
///fetch data and goto next
data = context->iter->data;
context->iter = g_slist_next(context->iter);
}
return data;
}
static void
zzip_archive_close(struct archive_file *file)
{
struct zzip_archive *context = (struct zzip_archive *) file;
if (!refcount_dec(&context->ref))
return;
if (context->list) {
//free list
for (GSList *tmp = context->list; tmp != NULL; tmp = g_slist_next(tmp))
g_free(tmp->data);
g_slist_free(context->list);
}
//close archive
zzip_dir_close (context->dir);
g_free(context);
}
/* single archive handling */
struct zzip_input_stream {
struct input_stream base;
struct zzip_archive *archive;
ZZIP_FILE *file;
};
static struct input_stream *
zzip_archive_open_stream(struct archive_file *file,
const char *pathname, GError **error_r)
{
struct zzip_archive *context = (struct zzip_archive *) file;
struct zzip_input_stream *zis;
ZZIP_STAT z_stat;
zis = g_new(struct zzip_input_stream, 1);
input_stream_init(&zis->base, &zzip_input_plugin, pathname);
zis->archive = context;
zis->file = zzip_file_open(context->dir, pathname, 0);
if (zis->file == NULL) {
g_free(zis);
g_set_error(error_r, zzip_quark(), 0,
"not found in the ZIP file: %s", pathname);
return NULL;
}
zis->base.ready = true;
//we are seekable (but its not recommendent to do so)
zis->base.seekable = true;
zzip_file_stat(zis->file, &z_stat);
zis->base.size = z_stat.st_size;
refcount_inc(&context->ref);
return &zis->base;
}
static void
zzip_input_close(struct input_stream *is)
{
struct zzip_input_stream *zis = (struct zzip_input_stream *)is;
zzip_file_close(zis->file);
zzip_archive_close(&zis->archive->base);
input_stream_deinit(&zis->base);
g_free(zis);
}
static size_t
zzip_input_read(struct input_stream *is, void *ptr, size_t size,
GError **error_r)
{
struct zzip_input_stream *zis = (struct zzip_input_stream *)is;
int ret;
ret = zzip_file_read(zis->file, ptr, size);
if (ret < 0) {
g_set_error(error_r, zzip_quark(), ret,
"zzip_file_read() has failed");
return 0;
}
is->offset = zzip_tell(zis->file);
return ret;
}
static bool
zzip_input_eof(struct input_stream *is)
{
struct zzip_input_stream *zis = (struct zzip_input_stream *)is;
return (goffset)zzip_tell(zis->file) == is->size;
}
static bool
zzip_input_seek(struct input_stream *is,
goffset offset, int whence, GError **error_r)
{
struct zzip_input_stream *zis = (struct zzip_input_stream *)is;
zzip_off_t ofs = zzip_seek(zis->file, offset, whence);
if (ofs != -1) {
g_set_error(error_r, zzip_quark(), ofs,
"zzip_seek() has failed");
is->offset = ofs;
return true;
}
return false;
}
/* exported structures */
static const char *const zzip_archive_extensions[] = {
"zip",
NULL
};
static const struct input_plugin zzip_input_plugin = {
.close = zzip_input_close,
.read = zzip_input_read,
.eof = zzip_input_eof,
.seek = zzip_input_seek,
};
const struct archive_plugin zzip_archive_plugin = {
.name = "zzip",
.open = zzip_archive_open,
.scan_reset = zzip_archive_scan_reset,
.scan_next = zzip_archive_scan_next,
.open_stream = zzip_archive_open_stream,
.close = zzip_archive_close,
.suffixes = zzip_archive_extensions
};

View File

@@ -0,0 +1,25 @@
/*
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program 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.
*
* This program 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 this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef MPD_ARCHIVE_ZZIP_H
#define MPD_ARCHIVE_ZZIP_H
extern const struct archive_plugin zzip_archive_plugin;
#endif

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2003-2009 The Music Player Daemon Project
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,6 +17,9 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h" /* must be first for large file support */
#include "archive_api.h"
#include <stdio.h>
#include <string.h>
@@ -26,8 +29,6 @@
#include <errno.h>
#include <glib.h>
#include "archive_api.h"
/**
*
* archive_lookup is used to determine if part of pathname refers to an regular

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2003-2009 The Music Player Daemon Project
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -27,72 +27,11 @@
*/
#include "archive_internal.h"
#include "archive_plugin.h"
#include "input_stream.h"
#include <stdbool.h>
struct archive_file;
struct archive_plugin {
const char *name;
/**
* optional, set this to NULL if the archive plugin doesn't
* have/need one this must false if there is an error and
* true otherwise
*/
bool (*init)(void);
/**
* optional, set this to NULL if the archive plugin doesn't
* have/need one
*/
void (*finish)(void);
/**
* tryes to open archive file and associates handle with archive
* returns pointer to handle used is all operations with this archive
* or NULL when opening fails
*/
struct archive_file *(*open)(char * pathname);
/**
* reset routine will move current read index in archive to default
* position and then the filenames from archives can be read
* via scan_next routine
*/
void (*scan_reset)(struct archive_file *);
/**
* the read method will return corresponding files from archive
* (as pathnames) and move read index to next file. When there is no
* next file it return NULL.
*/
char *(*scan_next)(struct archive_file *);
/**
* Opens an input_stream of a file within the archive.
*
* If this function succeeds, then the #input_stream "owns"
* the archive file and will automatically close it.
*
* @param path the path within the archive
*/
bool (*open_stream)(struct archive_file *, struct input_stream *is,
const char *path);
/**
* closes archive file.
*/
void (*close)(struct archive_file *);
/**
* suffixes handled by this plugin.
* last element in these arrays must always be a NULL
*/
const char *const*suffixes;
};
bool archive_lookup(char *pathname, char **archive, char **inpath, char **suffix);
#endif

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2003-2009 The Music Player Daemon Project
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -21,7 +21,14 @@
#define MPD_ARCHIVE_INTERNAL_H
struct archive_file {
int placeholder;
const struct archive_plugin *plugin;
};
static inline void
archive_file_init(struct archive_file *archive_file,
const struct archive_plugin *plugin)
{
archive_file->plugin = plugin;
}
#endif

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2003-2009 The Music Player Daemon Project
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,50 +17,44 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "archive_list.h"
#include "archive_api.h"
#include "utils.h"
#include "config.h"
#include "archive_list.h"
#include "archive_plugin.h"
#include "utils.h"
#include "archive/bz2_archive_plugin.h"
#include "archive/iso9660_archive_plugin.h"
#include "archive/zzip_archive_plugin.h"
#include <string.h>
#include <glib.h>
extern const struct archive_plugin bz2_plugin;
extern const struct archive_plugin zip_plugin;
extern const struct archive_plugin iso_plugin;
static const struct archive_plugin *const archive_plugins[] = {
#ifdef HAVE_BZ2
&bz2_plugin,
&bz2_archive_plugin,
#endif
#ifdef HAVE_ZIP
&zip_plugin,
#ifdef HAVE_ZZIP
&zzip_archive_plugin,
#endif
#ifdef HAVE_ISO
&iso_plugin,
#ifdef HAVE_ISO9660
&iso9660_archive_plugin,
#endif
NULL
};
enum {
num_archive_plugins = G_N_ELEMENTS(archive_plugins)-1,
};
/** which plugins have been initialized successfully? */
static bool archive_plugins_enabled[num_archive_plugins+1];
static bool archive_plugins_enabled[G_N_ELEMENTS(archive_plugins) - 1];
const struct archive_plugin *
archive_plugin_from_suffix(const char *suffix)
{
unsigned i;
if (suffix == NULL)
return NULL;
for (i=0; i < num_archive_plugins; ++i) {
for (unsigned i = 0; archive_plugins[i] != NULL; ++i) {
const struct archive_plugin *plugin = archive_plugins[i];
if (archive_plugins_enabled[i] &&
stringFoundInStringArray(plugin->suffixes, suffix)) {
plugin->suffixes != NULL &&
string_array_contains(plugin->suffixes, suffix)) {
++i;
return plugin;
}
@@ -71,7 +65,7 @@ archive_plugin_from_suffix(const char *suffix)
const struct archive_plugin *
archive_plugin_from_name(const char *name)
{
for (unsigned i = 0; i < num_archive_plugins; ++i) {
for (unsigned i = 0; archive_plugins[i] != NULL; ++i) {
const struct archive_plugin *plugin = archive_plugins[i];
if (archive_plugins_enabled[i] &&
strcmp(plugin->name, name) == 0)
@@ -84,7 +78,7 @@ void archive_plugin_print_all_suffixes(FILE * fp)
{
const char *const*suffixes;
for (unsigned i = 0; i < num_archive_plugins; ++i) {
for (unsigned i = 0; archive_plugins[i] != NULL; ++i) {
const struct archive_plugin *plugin = archive_plugins[i];
if (!archive_plugins_enabled[i])
continue;
@@ -101,7 +95,7 @@ void archive_plugin_print_all_suffixes(FILE * fp)
void archive_plugin_init_all(void)
{
for (unsigned i = 0; i < num_archive_plugins; ++i) {
for (unsigned i = 0; archive_plugins[i] != NULL; ++i) {
const struct archive_plugin *plugin = archive_plugins[i];
if (plugin->init == NULL || archive_plugins[i]->init())
archive_plugins_enabled[i] = true;
@@ -110,7 +104,7 @@ void archive_plugin_init_all(void)
void archive_plugin_deinit_all(void)
{
for (unsigned i = 0; i < num_archive_plugins; ++i) {
for (unsigned i = 0; archive_plugins[i] != NULL; ++i) {
const struct archive_plugin *plugin = archive_plugins[i];
if (archive_plugins_enabled[i] && plugin->finish != NULL)
archive_plugins[i]->finish();

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2003-2009 The Music Player Daemon Project
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -20,8 +20,6 @@
#ifndef MPD_ARCHIVE_LIST_H
#define MPD_ARCHIVE_LIST_H
#include "archive_api.h"
#include <stdio.h>
struct archive_plugin;

92
src/archive_plugin.c Normal file
View File

@@ -0,0 +1,92 @@
/*
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program 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.
*
* This program 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 this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "archive_plugin.h"
#include "archive_internal.h"
#include <assert.h>
struct archive_file *
archive_file_open(const struct archive_plugin *plugin, const char *path,
GError **error_r)
{
struct archive_file *file;
assert(plugin != NULL);
assert(plugin->open != NULL);
assert(path != NULL);
assert(error_r == NULL || *error_r == NULL);
file = plugin->open(path, error_r);
if (file != NULL) {
assert(file->plugin != NULL);
assert(file->plugin->close != NULL);
assert(file->plugin->scan_reset != NULL);
assert(file->plugin->scan_next != NULL);
assert(file->plugin->open_stream != NULL);
assert(error_r == NULL || *error_r == NULL);
} else {
assert(error_r == NULL || *error_r != NULL);
}
return file;
}
void
archive_file_close(struct archive_file *file)
{
assert(file != NULL);
assert(file->plugin != NULL);
assert(file->plugin->close != NULL);
file->plugin->close(file);
}
void
archive_file_scan_reset(struct archive_file *file)
{
assert(file != NULL);
assert(file->plugin != NULL);
assert(file->plugin->scan_reset != NULL);
assert(file->plugin->scan_next != NULL);
file->plugin->scan_reset(file);
}
char *
archive_file_scan_next(struct archive_file *file)
{
assert(file != NULL);
assert(file->plugin != NULL);
assert(file->plugin->scan_next != NULL);
return file->plugin->scan_next(file);
}
struct input_stream *
archive_file_open_stream(struct archive_file *file,
const char *path, GError **error_r)
{
assert(file != NULL);
assert(file->plugin != NULL);
assert(file->plugin->open_stream != NULL);
return file->plugin->open_stream(file, path, error_r);
}

107
src/archive_plugin.h Normal file
View File

@@ -0,0 +1,107 @@
/*
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program 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.
*
* This program 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 this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef MPD_ARCHIVE_PLUGIN_H
#define MPD_ARCHIVE_PLUGIN_H
#include <glib.h>
#include <stdbool.h>
struct input_stream;
struct archive_file;
struct archive_plugin {
const char *name;
/**
* optional, set this to NULL if the archive plugin doesn't
* have/need one this must false if there is an error and
* true otherwise
*/
bool (*init)(void);
/**
* optional, set this to NULL if the archive plugin doesn't
* have/need one
*/
void (*finish)(void);
/**
* tryes to open archive file and associates handle with archive
* returns pointer to handle used is all operations with this archive
* or NULL when opening fails
*/
struct archive_file *(*open)(const char *path_fs, GError **error_r);
/**
* reset routine will move current read index in archive to default
* position and then the filenames from archives can be read
* via scan_next routine
*/
void (*scan_reset)(struct archive_file *);
/**
* the read method will return corresponding files from archive
* (as pathnames) and move read index to next file. When there is no
* next file it return NULL.
*/
char *(*scan_next)(struct archive_file *);
/**
* Opens an input_stream of a file within the archive.
*
* @param path the path within the archive
* @param error_r location to store the error occuring, or
* NULL to ignore errors
*/
struct input_stream *(*open_stream)(struct archive_file *af,
const char *path,
GError **error_r);
/**
* closes archive file.
*/
void (*close)(struct archive_file *);
/**
* suffixes handled by this plugin.
* last element in these arrays must always be a NULL
*/
const char *const*suffixes;
};
struct archive_file *
archive_file_open(const struct archive_plugin *plugin, const char *path,
GError **error_r);
void
archive_file_close(struct archive_file *file);
void
archive_file_scan_reset(struct archive_file *file);
char *
archive_file_scan_next(struct archive_file *file);
struct input_stream *
archive_file_open_stream(struct archive_file *file,
const char *path, GError **error_r);
#endif

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2003-2009 The Music Player Daemon Project
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,6 +17,7 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h"
#include "audio.h"
#include "audio_format.h"
#include "audio_parser.h"
@@ -24,6 +25,7 @@
#include "output_plugin.h"
#include "output_all.h"
#include "conf.h"
#include "mpd_error.h"
#include <glib.h>
@@ -35,9 +37,8 @@ static struct audio_format configured_audio_format;
void getOutputAudioFormat(const struct audio_format *inAudioFormat,
struct audio_format *outAudioFormat)
{
*outAudioFormat = audio_format_defined(&configured_audio_format)
? configured_audio_format
: *inAudioFormat;
*outAudioFormat = *inAudioFormat;
audio_format_mask_apply(outAudioFormat, &configured_audio_format);
}
void initAudioConfig(void)
@@ -46,17 +47,13 @@ void initAudioConfig(void)
GError *error = NULL;
bool ret;
if (NULL == param || NULL == param->value)
if (param == NULL)
return;
ret = audio_format_parse(&configured_audio_format, param->value,
&error);
true, &error);
if (!ret)
g_error("error parsing \"%s\" at line %i: %s",
CONF_AUDIO_OUTPUT_FORMAT, param->line, error->message);
}
void finishAudioConfig(void)
{
audio_format_clear(&configured_audio_format);
MPD_ERROR("error parsing \"%s\" at line %i: %s",
CONF_AUDIO_OUTPUT_FORMAT, param->line,
error->message);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2003-2009 The Music Player Daemon Project
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -30,6 +30,4 @@ void getOutputAudioFormat(const struct audio_format *inFormat,
/* make sure initPlayerData is called before this function!! */
void initAudioConfig(void);
void finishAudioConfig(void);
#endif

74
src/audio_check.c Normal file
View File

@@ -0,0 +1,74 @@
/*
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program 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.
*
* This program 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 this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "audio_check.h"
#include "audio_format.h"
#include <assert.h>
bool
audio_check_sample_rate(unsigned long sample_rate, GError **error_r)
{
if (!audio_valid_sample_rate(sample_rate)) {
g_set_error(error_r, audio_format_quark(), 0,
"Invalid sample rate: %lu", sample_rate);
return false;
}
return true;
}
bool
audio_check_sample_format(enum sample_format sample_format, GError **error_r)
{
if (!audio_valid_sample_format(sample_format)) {
g_set_error(error_r, audio_format_quark(), 0,
"Invalid sample format: %u", sample_format);
return false;
}
return true;
}
bool
audio_check_channel_count(unsigned channels, GError **error_r)
{
if (!audio_valid_channel_count(channels)) {
g_set_error(error_r, audio_format_quark(), 0,
"Invalid channel count: %u", channels);
return false;
}
return true;
}
bool
audio_format_init_checked(struct audio_format *af, unsigned long sample_rate,
enum sample_format sample_format, unsigned channels,
GError **error_r)
{
if (audio_check_sample_rate(sample_rate, error_r) &&
audio_check_sample_format(sample_format, error_r) &&
audio_check_channel_count(channels, error_r)) {
audio_format_init(af, sample_rate, sample_format, channels);
assert(audio_format_valid(af));
return true;
} else
return false;
}

54
src/audio_check.h Normal file
View File

@@ -0,0 +1,54 @@
/*
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program 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.
*
* This program 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 this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef MPD_AUDIO_CHECK_H
#define MPD_AUDIO_CHECK_H
#include "audio_format.h"
#include <glib.h>
#include <stdbool.h>
/**
* The GLib quark used for errors reported by this library.
*/
static inline GQuark
audio_format_quark(void)
{
return g_quark_from_static_string("audio_format");
}
bool
audio_check_sample_rate(unsigned long sample_rate, GError **error_r);
bool
audio_check_sample_format(unsigned sample_format, GError **error_r);
bool
audio_check_channel_count(unsigned sample_format, GError **error_r);
/**
* Wrapper for audio_format_init(), which checks all attributes.
*/
bool
audio_format_init_checked(struct audio_format *af, unsigned long sample_rate,
enum sample_format sample_format, unsigned channels,
GError **error_r);
#endif

72
src/audio_format.c Normal file
View File

@@ -0,0 +1,72 @@
/*
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program 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.
*
* This program 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 this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "audio_format.h"
#include <assert.h>
#include <stdio.h>
#if G_BYTE_ORDER == G_BIG_ENDIAN
#define REVERSE_ENDIAN_SUFFIX "_le"
#else
#define REVERSE_ENDIAN_SUFFIX "_be"
#endif
const char *
sample_format_to_string(enum sample_format format)
{
switch (format) {
case SAMPLE_FORMAT_UNDEFINED:
return "?";
case SAMPLE_FORMAT_S8:
return "8";
case SAMPLE_FORMAT_S16:
return "16";
case SAMPLE_FORMAT_S24:
return "24_3";
case SAMPLE_FORMAT_S24_P32:
return "24";
case SAMPLE_FORMAT_S32:
return "32";
}
/* unreachable */
assert(false);
return "?";
}
const char *
audio_format_to_string(const struct audio_format *af,
struct audio_format_string *s)
{
assert(af != NULL);
assert(s != NULL);
snprintf(s->buffer, sizeof(s->buffer), "%u:%s%s:%u",
af->sample_rate, sample_format_to_string(af->format),
af->reverse_endian ? REVERSE_ENDIAN_SUFFIX : "",
af->channels);
return s->buffer;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2003-2009 The Music Player Daemon Project
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -23,24 +23,122 @@
#include <stdint.h>
#include <stdbool.h>
struct audio_format {
uint32_t sample_rate;
uint8_t bits;
uint8_t channels;
enum sample_format {
SAMPLE_FORMAT_UNDEFINED = 0,
SAMPLE_FORMAT_S8,
SAMPLE_FORMAT_S16,
/**
* Signed 24 bit integer samples, without padding.
*/
SAMPLE_FORMAT_S24,
/**
* Signed 24 bit integer samples, packed in 32 bit integers
* (the most significant byte is filled with the sign bit).
*/
SAMPLE_FORMAT_S24_P32,
SAMPLE_FORMAT_S32,
};
/**
* This structure describes the format of a raw PCM stream.
*/
struct audio_format {
/**
* The sample rate in Hz. A better name for this attribute is
* "frame rate", because technically, you have two samples per
* frame in stereo sound.
*/
uint32_t sample_rate;
/**
* The format samples are stored in. See the #sample_format
* enum for valid values.
*/
uint8_t format;
/**
* The number of channels. Only mono (1) and stereo (2) are
* fully supported currently.
*/
uint8_t channels;
/**
* If zero, then samples are stored in host byte order. If
* nonzero, then samples are stored in the reverse host byte
* order.
*/
uint8_t reverse_endian;
};
/**
* Buffer for audio_format_string().
*/
struct audio_format_string {
char buffer[24];
};
/**
* Clears the #audio_format object, i.e. sets all attributes to an
* undefined (invalid) value.
*/
static inline void audio_format_clear(struct audio_format *af)
{
af->sample_rate = 0;
af->bits = 0;
af->format = SAMPLE_FORMAT_UNDEFINED;
af->channels = 0;
af->reverse_endian = 0;
}
/**
* Initializes an #audio_format object, i.e. sets all
* attributes to valid values.
*/
static inline void audio_format_init(struct audio_format *af,
uint32_t sample_rate,
enum sample_format format, uint8_t channels)
{
af->sample_rate = sample_rate;
af->format = (uint8_t)format;
af->channels = channels;
af->reverse_endian = 0;
}
/**
* Checks whether the specified #audio_format object has a defined
* value.
*/
static inline bool audio_format_defined(const struct audio_format *af)
{
return af->sample_rate != 0;
}
/**
* Checks whether the specified #audio_format object is full, i.e. all
* attributes are defined. This is more complete than
* audio_format_defined(), but slower.
*/
static inline bool
audio_format_fully_defined(const struct audio_format *af)
{
return af->sample_rate != 0 && af->format != SAMPLE_FORMAT_UNDEFINED &&
af->channels != 0;
}
/**
* Checks whether the specified #audio_format object has at least one
* defined value.
*/
static inline bool
audio_format_mask_defined(const struct audio_format *af)
{
return af->sample_rate != 0 || af->format != SAMPLE_FORMAT_UNDEFINED ||
af->channels != 0;
}
/**
* Checks whether the sample rate is valid.
*
@@ -58,9 +156,21 @@ audio_valid_sample_rate(unsigned sample_rate)
* @param bits the number of significant bits per sample
*/
static inline bool
audio_valid_sample_format(unsigned bits)
audio_valid_sample_format(enum sample_format format)
{
return bits == 16 || bits == 24 || bits == 32 || bits == 8;
switch (format) {
case SAMPLE_FORMAT_S8:
case SAMPLE_FORMAT_S16:
case SAMPLE_FORMAT_S24:
case SAMPLE_FORMAT_S24_P32:
case SAMPLE_FORMAT_S32:
return true;
case SAMPLE_FORMAT_UNDEFINED:
break;
}
return false;
}
/**
@@ -79,16 +189,44 @@ audio_valid_channel_count(unsigned channels)
static inline bool audio_format_valid(const struct audio_format *af)
{
return audio_valid_sample_rate(af->sample_rate) &&
audio_valid_sample_format(af->bits) &&
audio_valid_sample_format((enum sample_format)af->format) &&
audio_valid_channel_count(af->channels);
}
/**
* Returns false if the format mask is not valid for playback with
* MPD. This function performs some basic validity checks.
*/
static inline bool audio_format_mask_valid(const struct audio_format *af)
{
return (af->sample_rate == 0 ||
audio_valid_sample_rate(af->sample_rate)) &&
(af->format == SAMPLE_FORMAT_UNDEFINED ||
audio_valid_sample_format((enum sample_format)af->format)) &&
(af->channels == 0 || audio_valid_channel_count(af->channels));
}
static inline bool audio_format_equals(const struct audio_format *a,
const struct audio_format *b)
{
return a->sample_rate == b->sample_rate &&
a->bits == b->bits &&
a->channels == b->channels;
a->format == b->format &&
a->channels == b->channels &&
a->reverse_endian == b->reverse_endian;
}
static inline void
audio_format_mask_apply(struct audio_format *af,
const struct audio_format *mask)
{
if (mask->sample_rate != 0)
af->sample_rate = mask->sample_rate;
if (mask->format != SAMPLE_FORMAT_UNDEFINED)
af->format = mask->format;
if (mask->channels != 0)
af->channels = mask->channels;
}
/**
@@ -96,28 +234,65 @@ static inline bool audio_format_equals(const struct audio_format *a,
*/
static inline unsigned audio_format_sample_size(const struct audio_format *af)
{
if (af->bits <= 8)
switch (af->format) {
case SAMPLE_FORMAT_S8:
return 1;
else if (af->bits <= 16)
case SAMPLE_FORMAT_S16:
return 2;
else
case SAMPLE_FORMAT_S24:
return 3;
case SAMPLE_FORMAT_S24_P32:
case SAMPLE_FORMAT_S32:
return 4;
case SAMPLE_FORMAT_UNDEFINED:
break;
}
return 0;
}
/**
* Returns the size of each full frame in bytes.
*/
static inline unsigned
audio_format_frame_size(const struct audio_format *af)
{
return audio_format_sample_size(af) * af->channels;
}
/**
* Returns the floating point factor which converts a time span to a
* storage size in bytes.
*/
static inline double audio_format_time_to_size(const struct audio_format *af)
{
return af->sample_rate * audio_format_frame_size(af);
}
static inline double audioFormatSizeToTime(const struct audio_format *af)
{
return 1.0 / audio_format_time_to_size(af);
}
/**
* Renders a #sample_format enum into a string, e.g. for printing it
* in a log file.
*
* @param format a #sample_format enum value
* @return the string
*/
const char *
sample_format_to_string(enum sample_format format);
/**
* Renders the #audio_format object into a string, e.g. for printing
* it in a log file.
*
* @param af the #audio_format object
* @param s a buffer to print into
* @return the string, or NULL if the #audio_format object is invalid
*/
const char *
audio_format_to_string(const struct audio_format *af,
struct audio_format_string *s);
#endif

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2003-2009 The Music Player Daemon Project
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -22,9 +22,13 @@
*
*/
#include "config.h"
#include "audio_parser.h"
#include "audio_format.h"
#include "audio_check.h"
#include <assert.h>
#include <string.h>
#include <stdlib.h>
/**
@@ -36,64 +40,158 @@ audio_parser_quark(void)
return g_quark_from_static_string("audio_parser");
}
bool
audio_format_parse(struct audio_format *dest, const char *src, GError **error)
static bool
parse_sample_rate(const char *src, bool mask, uint32_t *sample_rate_r,
const char **endptr_r, GError **error_r)
{
char *endptr;
unsigned long value;
char *endptr;
if (mask && *src == '*') {
*sample_rate_r = 0;
*endptr_r = src + 1;
return true;
}
value = strtoul(src, &endptr, 10);
if (endptr == src) {
g_set_error(error_r, audio_parser_quark(), 0,
"Failed to parse the sample rate");
return false;
} else if (!audio_check_sample_rate(value, error_r))
return false;
*sample_rate_r = value;
*endptr_r = endptr;
return true;
}
static bool
parse_sample_format(const char *src, bool mask,
enum sample_format *sample_format_r,
const char **endptr_r, GError **error_r)
{
unsigned long value;
char *endptr;
enum sample_format sample_format;
if (mask && *src == '*') {
*sample_format_r = SAMPLE_FORMAT_UNDEFINED;
*endptr_r = src + 1;
return true;
}
value = strtoul(src, &endptr, 10);
if (endptr == src) {
g_set_error(error_r, audio_parser_quark(), 0,
"Failed to parse the sample format");
return false;
}
switch (value) {
case 8:
sample_format = SAMPLE_FORMAT_S8;
break;
case 16:
sample_format = SAMPLE_FORMAT_S16;
break;
case 24:
if (memcmp(endptr, "_3", 2) == 0) {
sample_format = SAMPLE_FORMAT_S24;
endptr += 2;
} else
sample_format = SAMPLE_FORMAT_S24_P32;
break;
case 32:
sample_format = SAMPLE_FORMAT_S32;
break;
default:
g_set_error(error_r, audio_parser_quark(), 0,
"Invalid sample format: %lu", value);
return false;
}
assert(audio_valid_sample_format(sample_format));
*sample_format_r = sample_format;
*endptr_r = endptr;
return true;
}
static bool
parse_channel_count(const char *src, bool mask, uint8_t *channels_r,
const char **endptr_r, GError **error_r)
{
unsigned long value;
char *endptr;
if (mask && *src == '*') {
*channels_r = 0;
*endptr_r = src + 1;
return true;
}
value = strtoul(src, &endptr, 10);
if (endptr == src) {
g_set_error(error_r, audio_parser_quark(), 0,
"Failed to parse the channel count");
return false;
} else if (!audio_check_channel_count(value, error_r))
return false;
*channels_r = value;
*endptr_r = endptr;
return true;
}
bool
audio_format_parse(struct audio_format *dest, const char *src,
bool mask, GError **error_r)
{
uint32_t rate;
enum sample_format sample_format;
uint8_t channels;
audio_format_clear(dest);
/* parse sample rate */
value = strtoul(src, &endptr, 10);
if (endptr == src) {
g_set_error(error, audio_parser_quark(), 0,
"Sample rate missing");
if (!parse_sample_rate(src, mask, &rate, &src, error_r))
return false;
} else if (*endptr != ':') {
g_set_error(error, audio_parser_quark(), 0,
if (*src++ != ':') {
g_set_error(error_r, audio_parser_quark(), 0,
"Sample format missing");
return false;
} else if (!audio_valid_sample_rate(value)) {
g_set_error(error, audio_parser_quark(), 0,
"Invalid sample rate: %lu", value);
return false;
}
dest->sample_rate = value;
/* parse sample format */
src = endptr + 1;
value = strtoul(src, &endptr, 10);
if (endptr == src) {
g_set_error(error, audio_parser_quark(), 0,
"Sample format missing");
if (!parse_sample_format(src, mask, &sample_format, &src, error_r))
return false;
} else if (*endptr != ':') {
g_set_error(error, audio_parser_quark(), 0,
if (*src++ != ':') {
g_set_error(error_r, audio_parser_quark(), 0,
"Channel count missing");
return false;
} else if (!audio_valid_sample_format(value)) {
g_set_error(error, audio_parser_quark(), 0,
"Invalid sample format: %lu", value);
return false;
}
dest->bits = value;
/* parse channel count */
src = endptr + 1;
value = strtoul(src, &endptr, 10);
if (*endptr != 0 || !audio_valid_channel_count(value)) {
g_set_error(error, audio_parser_quark(), 0,
"Invalid channel count: %s", src);
if (!parse_channel_count(src, mask, &channels, &src, error_r))
return false;
if (*src != 0) {
g_set_error(error_r, audio_parser_quark(), 0,
"Extra data after channel count: %s", src);
return false;
}
dest->channels = value;
audio_format_init(dest, rate, sample_format, channels);
return true;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2003-2009 The Music Player Daemon Project
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -37,11 +37,13 @@ struct audio_format;
*
* @param dest the destination #audio_format struct
* @param src the input string
* @param error location to store the error occuring, or NULL to
* @param mask if true, then "*" is allowed for any number of items
* @param error_r location to store the error occuring, or NULL to
* ignore errors
* @return true on success
*/
bool
audio_format_parse(struct audio_format *dest, const char *src, GError **error);
audio_format_parse(struct audio_format *dest, const char *src,
bool mask, GError **error_r);
#endif

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2003-2009 The Music Player Daemon Project
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,6 +17,7 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h"
#include "buffer.h"
#include "chunk.h"
#include "poison.h"
@@ -117,6 +118,9 @@ music_buffer_return(struct music_buffer *buffer, struct music_chunk *chunk)
assert(buffer != NULL);
assert(chunk != NULL);
if (chunk->other != NULL)
music_buffer_return(buffer, chunk->other);
g_mutex_lock(buffer->mutex);
music_chunk_free(chunk);

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2003-2009 The Music Player Daemon Project
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify

View File

@@ -1,133 +0,0 @@
/*
* Copyright (C) 2003-2009 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program 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.
*
* This program 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 this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "buffer2array.h"
#include <glib.h>
#include <string.h>
int buffer2array(char *buffer, char *array[], const int max)
{
int i = 0;
char *c = buffer;
while (*c != '\0' && i < max) {
if (*c == '\"') {
array[i++] = ++c;
while (*c != '\0') {
if (*c == '\"') {
*(c++) = '\0';
break;
}
else if (*(c++) == '\\' && *c != '\0') {
memmove(c - 1, c, strlen(c) + 1);
}
}
} else {
c = g_strchug(c);
if (*c == '\0')
return i;
array[i++] = c++;
while (!g_ascii_isspace(*c) && *c != '\0')
++c;
}
if (*c == '\0')
return i;
*(c++) = '\0';
c = g_strchug(c);
}
return i;
}
#ifdef UNIT_TEST
#include <stdio.h>
#include <string.h>
#include <assert.h>
int main()
{
char *a[4] = { NULL };
char *b;
int max;
b = strdup("lsinfo \"/some/dir/name \\\"test\\\"\"");
assert(b);
max = buffer2array(b, a, 4);
assert( !strcmp("lsinfo", a[0]) );
assert( !strcmp("/some/dir/name \"test\"", a[1]) );
assert( !a[2] );
b = strdup("lsinfo \"/some/dir/name \\\"test\\\" something else\"");
assert(b);
max = buffer2array(b, a, 4);
assert( !strcmp("lsinfo", a[0]) );
assert( !strcmp("/some/dir/name \"test\" something else", a[1]) );
assert( !a[2] );
b = strdup("lsinfo \"/some/dir\\\\name\"");
assert(b);
max = buffer2array(b, a, 4);
assert( !strcmp("lsinfo", a[0]) );
assert( !strcmp("/some/dir\\name", a[1]) );
assert( !a[2] );
b = strdup("lsinfo \"/some/dir name\"");
assert(b);
max = buffer2array(b, a, 4);
assert( !strcmp("lsinfo", a[0]) );
assert( !strcmp("/some/dir name", a[1]) );
assert( !a[2] );
b = strdup("lsinfo \"\\\"/some/dir\\\"\"");
assert(b);
max = buffer2array(b, a, 4);
assert( !strcmp("lsinfo", a[0]) );
assert( !strcmp("\"/some/dir\"", a[1]) );
assert( !a[2] );
b = strdup("lsinfo \"\\\"/some/dir\\\" x\"");
assert(b);
max = buffer2array(b, a, 4);
assert( !strcmp("lsinfo", a[0]) );
assert( !strcmp("\"/some/dir\" x", a[1]) );
assert( !a[2] );
b = strdup("lsinfo \"single quote\\'d from php magicquotes\"");
assert(b);
max = buffer2array(b, a, 4);
assert( !strcmp("lsinfo", a[0]) );
assert( !strcmp("single quote\'d from php magicquotes", a[1]) );
assert( !a[2] );
b = strdup("lsinfo \"double quote\\\"d from php magicquotes\"");
assert(b);
max = buffer2array(b, a, 4);
assert( !strcmp("lsinfo", a[0]) );
assert( !strcmp("double quote\"d from php magicquotes", a[1]) );
assert( !a[2] );
return 0;
}
#endif

47
src/check.h Normal file
View File

@@ -0,0 +1,47 @@
/*
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program 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.
*
* This program 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 this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef MPD_CHECK_H
#define MPD_CHECK_H
/*
* All sources must include config.h on the first line to ensure that
* Large File Support is configured properly. This header checks
* whether this has happened.
*
* Usage: include this header before you use any of the above types.
* It will stop the compiler if something went wrong.
*
* This is Linux/glibc specific, and only enabled in the debug build,
* so bugs in this headers don't affect users with production builds.
*
*/
#ifndef PACKAGE_VERSION
#error config.h missing
#endif
#if defined(__linux__) && !defined(NDEBUG) && defined(ENABLE_LARGEFILE) && \
defined(_FEATURES_H) && defined(__i386__) && \
!defined(__USE_FILE_OFFSET64)
/* on i386, check if LFS is enabled */
#error config.h was included too late
#endif
#endif

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2003-2009 The Music Player Daemon Project
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,6 +17,7 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h"
#include "chunk.h"
#include "audio_format.h"
#include "tag.h"
@@ -26,8 +27,10 @@
void
music_chunk_init(struct music_chunk *chunk)
{
chunk->other = NULL;
chunk->length = 0;
chunk->tag = NULL;
chunk->replay_gain_serial = 0;
}
void

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2003-2009 The Music Player Daemon Project
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -20,6 +20,8 @@
#ifndef MPD_CHUNK_H
#define MPD_CHUNK_H
#include "replay_gain_info.h"
#ifndef NDEBUG
#include "audio_format.h"
#endif
@@ -42,6 +44,18 @@ struct music_chunk {
/** the next chunk in a linked list */
struct music_chunk *next;
/**
* An optional chunk which should be mixed into this chunk.
* This is used for cross-fading.
*/
struct music_chunk *other;
/**
* The current mix ratio for cross-fading: 1.0 means play 100%
* of this chunk, 0.0 means play 100% of the "other" chunk.
*/
float mix_ratio;
/** number of bytes stored in this chunk */
uint16_t length;
@@ -59,6 +73,19 @@ struct music_chunk {
*/
struct tag *tag;
/**
* Replay gain information associated with this chunk.
* Only valid if the serial is not 0.
*/
struct replay_gain_info replay_gain_info;
/**
* A serial number for checking if replay gain info has
* changed since the last chunk. The magic value 0 indicates
* that there is no replay gain info available.
*/
unsigned replay_gain_serial;
/** the data (probably PCM) */
char data[CHUNK_SIZE];

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2003-2009 The Music Player Daemon Project
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,110 +17,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "client.h"
#include "fifo_buffer.h"
#include "command.h"
#include "conf.h"
#include "listen.h"
#include "socket_util.h"
#include "permission.h"
#include "event_pipe.h"
#include "idle.h"
#include "main.h"
#include "config.h"
#include <glib.h>
#include <assert.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#ifdef WIN32
#include <ws2tcpip.h>
#include <winsock.h>
#else
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#endif
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "client"
#define LOG_LEVEL_SECURE G_LOG_LEVEL_INFO
static const char GREETING[] = "OK MPD " PROTOCOL_VERSION "\n";
#define CLIENT_LIST_MODE_BEGIN "command_list_begin"
#define CLIENT_LIST_OK_MODE_BEGIN "command_list_ok_begin"
#define CLIENT_LIST_MODE_END "command_list_end"
#define CLIENT_TIMEOUT_DEFAULT (60)
#define CLIENT_MAX_CONNECTIONS_DEFAULT (10)
#define CLIENT_MAX_COMMAND_LIST_DEFAULT (2048*1024)
#define CLIENT_MAX_OUTPUT_BUFFER_SIZE_DEFAULT (8192*1024)
/* set this to zero to indicate we have no possible clients */
static unsigned int client_max_connections; /*CLIENT_MAX_CONNECTIONS_DEFAULT; */
static int client_timeout;
static size_t client_max_command_list_size;
static size_t client_max_output_buffer_size;
struct deferred_buffer {
size_t size;
char data[sizeof(long)];
};
struct client {
GIOChannel *channel;
guint source_id;
/** the buffer for reading lines from the #channel */
struct fifo_buffer *input;
unsigned permission;
/** the uid of the client process, or -1 if unknown */
int uid;
/**
* How long since the last activity from this client?
*/
GTimer *last_activity;
GSList *cmd_list; /* for when in list mode */
int cmd_list_OK; /* print OK after each command execution */
size_t cmd_list_size; /* mem cmd_list consumes */
GQueue *deferred_send; /* for output if client is slow */
size_t deferred_bytes; /* mem deferred_send consumes */
unsigned int num; /* client number */
char send_buf[4096];
size_t send_buf_used; /* bytes used this instance */
/** is this client waiting for an "idle" response? */
bool idle_waiting;
/** idle flags pending on this client, to be sent as soon as
the client enters "idle" */
unsigned idle_flags;
/** idle flags that the client wants to receive */
unsigned idle_subscriptions;
};
static GList *clients;
static unsigned num_clients;
static guint expire_source_id;
static void client_write_deferred(struct client *client);
static void client_write_output(struct client *client);
static void client_manager_expire(void);
static gboolean
client_in_event(GIOChannel *source, GIOCondition condition, gpointer data);
#include "client_internal.h"
bool client_is_expired(const struct client *client)
{
@@ -141,782 +39,3 @@ void client_set_permission(struct client *client, unsigned permission)
{
client->permission = permission;
}
/**
* An idle event which calls client_manager_expire().
*/
static gboolean
client_manager_expire_event(G_GNUC_UNUSED gpointer data)
{
expire_source_id = 0;
client_manager_expire();
return false;
}
static inline void client_set_expired(struct client *client)
{
if (expire_source_id == 0 && !client_is_expired(client))
/* delayed deletion */
expire_source_id = g_idle_add(client_manager_expire_event,
NULL);
if (client->source_id != 0) {
g_source_remove(client->source_id);
client->source_id = 0;
}
if (client->channel != NULL) {
g_io_channel_unref(client->channel);
client->channel = NULL;
}
}
static void client_init(struct client *client, int fd)
{
static unsigned int next_client_num;
assert(fd >= 0);
client->cmd_list_size = 0;
client->cmd_list_OK = -1;
#ifndef G_OS_WIN32
client->channel = g_io_channel_unix_new(fd);
#else
client->channel = g_io_channel_win32_new_socket(fd);
#endif
/* GLib is responsible for closing the file descriptor */
g_io_channel_set_close_on_unref(client->channel, true);
/* NULL encoding means the stream is binary safe; the MPD
protocol is UTF-8 only, but we are doing this call anyway
to prevent GLib from messing around with the stream */
g_io_channel_set_encoding(client->channel, NULL, NULL);
/* we prefer to do buffering */
g_io_channel_set_buffered(client->channel, false);
client->source_id = g_io_add_watch(client->channel,
G_IO_IN|G_IO_ERR|G_IO_HUP,
client_in_event, client);
client->input = fifo_buffer_new(4096);
client->cmd_list = NULL;
client->deferred_send = g_queue_new();
client->deferred_bytes = 0;
client->num = next_client_num++;
client->send_buf_used = 0;
client->permission = getDefaultPermissions();
(void)write(fd, GREETING, sizeof(GREETING) - 1);
}
static void free_cmd_list(GSList *list)
{
for (GSList *tmp = list; tmp != NULL; tmp = g_slist_next(tmp))
g_free(tmp->data);
g_slist_free(list);
}
static void new_cmd_list_ptr(struct client *client, char *s)
{
client->cmd_list = g_slist_prepend(client->cmd_list, g_strdup(s));
}
static void
deferred_buffer_free(gpointer data, G_GNUC_UNUSED gpointer user_data)
{
struct deferred_buffer *buffer = data;
g_free(buffer);
}
static void client_close(struct client *client)
{
assert(num_clients > 0);
assert(clients != NULL);
clients = g_list_remove(clients, client);
--num_clients;
client_set_expired(client);
g_timer_destroy(client->last_activity);
if (client->cmd_list) {
free_cmd_list(client->cmd_list);
client->cmd_list = NULL;
}
g_queue_foreach(client->deferred_send, deferred_buffer_free, NULL);
g_queue_free(client->deferred_send);
fifo_buffer_free(client->input);
g_log(G_LOG_DOMAIN, LOG_LEVEL_SECURE,
"[%u] closed", client->num);
g_free(client);
}
void client_new(int fd, const struct sockaddr *sa, size_t sa_length, int uid)
{
struct client *client;
char *remote;
if (num_clients >= client_max_connections) {
g_warning("Max Connections Reached!");
close(fd);
return;
}
client = g_new0(struct client, 1);
clients = g_list_prepend(clients, client);
++num_clients;
client_init(client, fd);
client->uid = uid;
client->last_activity = g_timer_new();
remote = sockaddr_to_string(sa, sa_length, NULL);
g_log(G_LOG_DOMAIN, LOG_LEVEL_SECURE,
"[%u] opened from %s", client->num, remote);
g_free(remote);
}
static int client_process_line(struct client *client, char *line)
{
int ret = 1;
if (strcmp(line, "noidle") == 0) {
if (client->idle_waiting) {
/* send empty idle response and leave idle mode */
client->idle_waiting = false;
command_success(client);
client_write_output(client);
}
/* do nothing if the client wasn't idling: the client
has already received the full idle response from
client_idle_notify(), which he can now evaluate */
return 0;
} else if (client->idle_waiting) {
/* during idle mode, clients must not send anything
except "noidle" */
g_warning("[%u] command \"%s\" during idle",
client->num, line);
return COMMAND_RETURN_CLOSE;
}
if (client->cmd_list_OK >= 0) {
if (strcmp(line, CLIENT_LIST_MODE_END) == 0) {
g_debug("[%u] process command list",
client->num);
/* for scalability reasons, we have prepended
each new command; now we have to reverse it
to restore the correct order */
client->cmd_list = g_slist_reverse(client->cmd_list);
ret = command_process_list(client,
client->cmd_list_OK,
client->cmd_list);
g_debug("[%u] process command "
"list returned %i", client->num, ret);
if (ret == COMMAND_RETURN_CLOSE ||
client_is_expired(client))
return COMMAND_RETURN_CLOSE;
if (ret == 0)
command_success(client);
client_write_output(client);
free_cmd_list(client->cmd_list);
client->cmd_list = NULL;
client->cmd_list_OK = -1;
} else {
size_t len = strlen(line) + 1;
client->cmd_list_size += len;
if (client->cmd_list_size >
client_max_command_list_size) {
g_warning("[%u] command list size (%lu) "
"is larger than the max (%lu)",
client->num,
(unsigned long)client->cmd_list_size,
(unsigned long)client_max_command_list_size);
return COMMAND_RETURN_CLOSE;
} else
new_cmd_list_ptr(client, line);
}
} else {
if (strcmp(line, CLIENT_LIST_MODE_BEGIN) == 0) {
client->cmd_list_OK = 0;
ret = 1;
} else if (strcmp(line, CLIENT_LIST_OK_MODE_BEGIN) == 0) {
client->cmd_list_OK = 1;
ret = 1;
} else {
g_debug("[%u] process command \"%s\"",
client->num, line);
ret = command_process(client, line);
g_debug("[%u] command returned %i",
client->num, ret);
if (ret == COMMAND_RETURN_CLOSE ||
client_is_expired(client))
return COMMAND_RETURN_CLOSE;
if (ret == 0)
command_success(client);
client_write_output(client);
}
}
return ret;
}
static char *
client_read_line(struct client *client)
{
const char *p, *newline;
size_t length;
char *line;
p = fifo_buffer_read(client->input, &length);
if (p == NULL)
return NULL;
newline = memchr(p, '\n', length);
if (newline == NULL)
return NULL;
line = g_strndup(p, newline - p);
fifo_buffer_consume(client->input, newline - p + 1);
return g_strchomp(line);
}
static int client_input_received(struct client *client, size_t bytesRead)
{
char *line;
int ret;
fifo_buffer_append(client->input, bytesRead);
/* process all lines */
while ((line = client_read_line(client)) != NULL) {
ret = client_process_line(client, line);
g_free(line);
if (ret == COMMAND_RETURN_KILL ||
ret == COMMAND_RETURN_CLOSE)
return ret;
if (client_is_expired(client))
return COMMAND_RETURN_CLOSE;
}
return 0;
}
static int client_read(struct client *client)
{
char *p;
size_t max_length;
GError *error = NULL;
GIOStatus status;
gsize bytes_read;
assert(client != NULL);
assert(client->channel != NULL);
p = fifo_buffer_write(client->input, &max_length);
if (p == NULL) {
g_warning("[%u] buffer overflow", client->num);
return COMMAND_RETURN_CLOSE;
}
status = g_io_channel_read_chars(client->channel, p, max_length,
&bytes_read, &error);
switch (status) {
case G_IO_STATUS_NORMAL:
return client_input_received(client, bytes_read);
case G_IO_STATUS_AGAIN:
/* try again later, after select() */
return 0;
case G_IO_STATUS_EOF:
/* peer disconnected */
return COMMAND_RETURN_CLOSE;
case G_IO_STATUS_ERROR:
/* I/O error */
g_warning("failed to read from client %d: %s",
client->num, error->message);
g_error_free(error);
return COMMAND_RETURN_CLOSE;
}
/* unreachable */
return COMMAND_RETURN_CLOSE;
}
static gboolean
client_out_event(G_GNUC_UNUSED GIOChannel *source, GIOCondition condition,
gpointer data);
static gboolean
client_in_event(G_GNUC_UNUSED GIOChannel *source,
GIOCondition condition,
gpointer data)
{
struct client *client = data;
int ret;
assert(!client_is_expired(client));
if (condition != G_IO_IN) {
client_set_expired(client);
return false;
}
g_timer_start(client->last_activity);
ret = client_read(client);
switch (ret) {
case COMMAND_RETURN_KILL:
client_close(client);
g_main_loop_quit(main_loop);
return false;
case COMMAND_RETURN_CLOSE:
client_close(client);
return false;
}
if (client_is_expired(client)) {
client_close(client);
return false;
}
if (!g_queue_is_empty(client->deferred_send)) {
/* deferred buffers exist: schedule write */
client->source_id = g_io_add_watch(client->channel,
G_IO_OUT|G_IO_ERR|G_IO_HUP,
client_out_event, client);
return false;
}
/* read more */
return true;
}
static gboolean
client_out_event(G_GNUC_UNUSED GIOChannel *source, GIOCondition condition,
gpointer data)
{
struct client *client = data;
assert(!client_is_expired(client));
if (condition != G_IO_OUT) {
client_set_expired(client);
return false;
}
client_write_deferred(client);
if (client_is_expired(client)) {
client_close(client);
return false;
}
g_timer_start(client->last_activity);
if (g_queue_is_empty(client->deferred_send)) {
/* done sending deferred buffers exist: schedule
read */
client->source_id = g_io_add_watch(client->channel,
G_IO_IN|G_IO_ERR|G_IO_HUP,
client_in_event, client);
return false;
}
/* write more */
return true;
}
void client_manager_init(void)
{
client_timeout = config_get_positive(CONF_CONN_TIMEOUT,
CLIENT_TIMEOUT_DEFAULT);
client_max_connections =
config_get_positive(CONF_MAX_CONN,
CLIENT_MAX_CONNECTIONS_DEFAULT);
client_max_command_list_size =
config_get_positive(CONF_MAX_COMMAND_LIST_SIZE,
CLIENT_MAX_COMMAND_LIST_DEFAULT / 1024)
* 1024;
client_max_output_buffer_size =
config_get_positive(CONF_MAX_OUTPUT_BUFFER_SIZE,
CLIENT_MAX_OUTPUT_BUFFER_SIZE_DEFAULT / 1024)
* 1024;
}
static void client_close_all(void)
{
while (clients != NULL) {
struct client *client = clients->data;
client_close(client);
}
assert(num_clients == 0);
}
void client_manager_deinit(void)
{
client_close_all();
client_max_connections = 0;
if (expire_source_id != 0)
g_source_remove(expire_source_id);
}
static void
client_check_expired_callback(gpointer data, G_GNUC_UNUSED gpointer user_data)
{
struct client *client = data;
if (client_is_expired(client)) {
g_debug("[%u] expired", client->num);
client_close(client);
} else if (!client->idle_waiting && /* idle clients
never expire */
(int)g_timer_elapsed(client->last_activity, NULL) >
client_timeout) {
g_debug("[%u] timeout", client->num);
client_close(client);
}
}
static void
client_manager_expire(void)
{
g_list_foreach(clients, client_check_expired_callback, NULL);
}
static size_t
client_write_deferred_buffer(struct client *client,
const struct deferred_buffer *buffer)
{
GError *error = NULL;
GIOStatus status;
gsize bytes_written;
assert(client != NULL);
assert(client->channel != NULL);
assert(buffer != NULL);
status = g_io_channel_write_chars
(client->channel, buffer->data, buffer->size,
&bytes_written, &error);
switch (status) {
case G_IO_STATUS_NORMAL:
return bytes_written;
case G_IO_STATUS_AGAIN:
return 0;
case G_IO_STATUS_EOF:
/* client has disconnected */
client_set_expired(client);
return 0;
case G_IO_STATUS_ERROR:
/* I/O error */
client_set_expired(client);
g_warning("failed to flush buffer for %i: %s",
client->num, error->message);
g_error_free(error);
return 0;
}
/* unreachable */
return 0;
}
static void client_write_deferred(struct client *client)
{
size_t ret;
while (!g_queue_is_empty(client->deferred_send)) {
struct deferred_buffer *buf =
g_queue_peek_head(client->deferred_send);
assert(buf->size > 0);
assert(buf->size <= client->deferred_bytes);
ret = client_write_deferred_buffer(client, buf);
if (ret == 0)
break;
if (ret < buf->size) {
assert(client->deferred_bytes >= (size_t)ret);
client->deferred_bytes -= ret;
buf->size -= ret;
memmove(buf->data, buf->data + ret, buf->size);
break;
} else {
size_t decr = sizeof(*buf) -
sizeof(buf->data) + buf->size;
assert(client->deferred_bytes >= decr);
client->deferred_bytes -= decr;
g_free(buf);
g_queue_pop_head(client->deferred_send);
}
g_timer_start(client->last_activity);
}
if (g_queue_is_empty(client->deferred_send)) {
g_debug("[%u] buffer empty %lu", client->num,
(unsigned long)client->deferred_bytes);
assert(client->deferred_bytes == 0);
}
}
static void client_defer_output(struct client *client,
const void *data, size_t length)
{
size_t alloc;
struct deferred_buffer *buf;
assert(length > 0);
alloc = sizeof(*buf) - sizeof(buf->data) + length;
client->deferred_bytes += alloc;
if (client->deferred_bytes > client_max_output_buffer_size) {
g_warning("[%u] output buffer size (%lu) is "
"larger than the max (%lu)",
client->num,
(unsigned long)client->deferred_bytes,
(unsigned long)client_max_output_buffer_size);
/* cause client to close */
client_set_expired(client);
return;
}
buf = g_malloc(alloc);
buf->size = length;
memcpy(buf->data, data, length);
g_queue_push_tail(client->deferred_send, buf);
}
static void client_write_direct(struct client *client,
const char *data, size_t length)
{
GError *error = NULL;
GIOStatus status;
gsize bytes_written;
assert(client != NULL);
assert(client->channel != NULL);
assert(data != NULL);
assert(length > 0);
assert(g_queue_is_empty(client->deferred_send));
status = g_io_channel_write_chars(client->channel, data, length,
&bytes_written, &error);
switch (status) {
case G_IO_STATUS_NORMAL:
case G_IO_STATUS_AGAIN:
break;
case G_IO_STATUS_EOF:
/* client has disconnected */
client_set_expired(client);
return;
case G_IO_STATUS_ERROR:
/* I/O error */
client_set_expired(client);
g_warning("failed to write to %i: %s",
client->num, error->message);
g_error_free(error);
return;
}
if (bytes_written < length)
client_defer_output(client, data + bytes_written,
length - bytes_written);
if (!g_queue_is_empty(client->deferred_send))
g_debug("[%u] buffer created", client->num);
}
static void client_write_output(struct client *client)
{
if (client_is_expired(client) || !client->send_buf_used)
return;
if (!g_queue_is_empty(client->deferred_send)) {
client_defer_output(client, client->send_buf,
client->send_buf_used);
if (client_is_expired(client))
return;
/* try to flush the deferred buffers now; the current
server command may take too long to finish, and
meanwhile try to feed output to the client,
otherwise it will time out. One reason why
deferring is slow might be that currently each
client_write() allocates a new deferred buffer.
This should be optimized after MPD 0.14. */
client_write_deferred(client);
} else
client_write_direct(client, client->send_buf,
client->send_buf_used);
client->send_buf_used = 0;
}
/**
* Write a block of data to the client.
*/
static void client_write(struct client *client, const char *buffer, size_t buflen)
{
/* if the client is going to be closed, do nothing */
if (client_is_expired(client))
return;
while (buflen > 0 && !client_is_expired(client)) {
size_t copylen;
assert(client->send_buf_used < sizeof(client->send_buf));
copylen = sizeof(client->send_buf) - client->send_buf_used;
if (copylen > buflen)
copylen = buflen;
memcpy(client->send_buf + client->send_buf_used, buffer,
copylen);
buflen -= copylen;
client->send_buf_used += copylen;
buffer += copylen;
if (client->send_buf_used >= sizeof(client->send_buf))
client_write_output(client);
}
}
void client_puts(struct client *client, const char *s)
{
client_write(client, s, strlen(s));
}
void client_vprintf(struct client *client, const char *fmt, va_list args)
{
va_list tmp;
int length;
char *buffer;
va_copy(tmp, args);
length = vsnprintf(NULL, 0, fmt, tmp);
va_end(tmp);
if (length <= 0)
/* wtf.. */
return;
buffer = g_malloc(length + 1);
vsnprintf(buffer, length + 1, fmt, args);
client_write(client, buffer, length);
g_free(buffer);
}
G_GNUC_PRINTF(2, 3) void client_printf(struct client *client, const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
client_vprintf(client, fmt, args);
va_end(args);
}
/**
* Send "idle" response to this client.
*/
static void
client_idle_notify(struct client *client)
{
unsigned flags, i;
const char *const* idle_names;
assert(client->idle_waiting);
assert(client->idle_flags != 0);
flags = client->idle_flags;
client->idle_flags = 0;
client->idle_waiting = false;
idle_names = idle_get_names();
for (i = 0; idle_names[i]; ++i) {
if (flags & (1 << i) & client->idle_subscriptions)
client_printf(client, "changed: %s\n",
idle_names[i]);
}
client_puts(client, "OK\n");
g_timer_start(client->last_activity);
}
static void
client_idle_callback(gpointer data, gpointer user_data)
{
struct client *client = data;
unsigned flags = GPOINTER_TO_UINT(user_data);
if (client_is_expired(client))
return;
client->idle_flags |= flags;
if (client->idle_waiting
&& (client->idle_flags & client->idle_subscriptions)) {
client_idle_notify(client);
client_write_output(client);
}
}
void client_manager_idle_add(unsigned flags)
{
assert(flags != 0);
g_list_foreach(clients, client_idle_callback, GUINT_TO_POINTER(flags));
}
bool client_idle_wait(struct client *client, unsigned flags)
{
assert(!client->idle_waiting);
client->idle_waiting = true;
client->idle_subscriptions = flags;
if (client->idle_flags & client->idle_subscriptions) {
client_idle_notify(client);
return true;
} else
return false;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2003-2009 The Music Player Daemon Project
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify

108
src/client_event.c Normal file
View File

@@ -0,0 +1,108 @@
/*
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program 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.
*
* This program 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 this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h"
#include "client_internal.h"
#include "main.h"
#include <assert.h>
static gboolean
client_out_event(G_GNUC_UNUSED GIOChannel *source, GIOCondition condition,
gpointer data)
{
struct client *client = data;
assert(!client_is_expired(client));
if (condition != G_IO_OUT) {
client_set_expired(client);
return false;
}
client_write_deferred(client);
if (client_is_expired(client)) {
client_close(client);
return false;
}
g_timer_start(client->last_activity);
if (g_queue_is_empty(client->deferred_send)) {
/* done sending deferred buffers exist: schedule
read */
client->source_id = g_io_add_watch(client->channel,
G_IO_IN|G_IO_ERR|G_IO_HUP,
client_in_event, client);
return false;
}
/* write more */
return true;
}
gboolean
client_in_event(G_GNUC_UNUSED GIOChannel *source, GIOCondition condition,
gpointer data)
{
struct client *client = data;
enum command_return ret;
assert(!client_is_expired(client));
if (condition != G_IO_IN) {
client_set_expired(client);
return false;
}
g_timer_start(client->last_activity);
ret = client_read(client);
switch (ret) {
case COMMAND_RETURN_OK:
case COMMAND_RETURN_ERROR:
break;
case COMMAND_RETURN_KILL:
client_close(client);
g_main_loop_quit(main_loop);
return false;
case COMMAND_RETURN_CLOSE:
client_close(client);
return false;
}
if (client_is_expired(client)) {
client_close(client);
return false;
}
if (!g_queue_is_empty(client->deferred_send)) {
/* deferred buffers exist: schedule write */
client->source_id = g_io_add_watch(client->channel,
G_IO_OUT|G_IO_ERR|G_IO_HUP,
client_out_event, client);
return false;
}
/* read more */
return true;
}

90
src/client_expire.c Normal file
View File

@@ -0,0 +1,90 @@
/*
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program 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.
*
* This program 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 this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h"
#include "client_internal.h"
static guint expire_source_id;
void
client_set_expired(struct client *client)
{
if (!client_is_expired(client))
client_schedule_expire();
if (client->source_id != 0) {
g_source_remove(client->source_id);
client->source_id = 0;
}
if (client->channel != NULL) {
g_io_channel_unref(client->channel);
client->channel = NULL;
}
}
static void
client_check_expired_callback(gpointer data, G_GNUC_UNUSED gpointer user_data)
{
struct client *client = data;
if (client_is_expired(client)) {
g_debug("[%u] expired", client->num);
client_close(client);
} else if (!client->idle_waiting && /* idle clients
never expire */
(int)g_timer_elapsed(client->last_activity, NULL) >
client_timeout) {
g_debug("[%u] timeout", client->num);
client_close(client);
}
}
static void
client_manager_expire(void)
{
client_list_foreach(client_check_expired_callback, NULL);
}
/**
* An idle event which calls client_manager_expire().
*/
static gboolean
client_manager_expire_event(G_GNUC_UNUSED gpointer data)
{
expire_source_id = 0;
client_manager_expire();
return false;
}
void
client_schedule_expire(void)
{
if (expire_source_id == 0)
/* delayed deletion */
expire_source_id = g_idle_add(client_manager_expire_event,
NULL);
}
void
client_deinit_expire(void)
{
if (expire_source_id != 0)
g_source_remove(expire_source_id);
}

73
src/client_global.c Normal file
View File

@@ -0,0 +1,73 @@
/*
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program 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.
*
* This program 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 this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h"
#include "client_internal.h"
#include "conf.h"
#include <assert.h>
#define CLIENT_TIMEOUT_DEFAULT (60)
#define CLIENT_MAX_CONNECTIONS_DEFAULT (10)
#define CLIENT_MAX_COMMAND_LIST_DEFAULT (2048*1024)
#define CLIENT_MAX_OUTPUT_BUFFER_SIZE_DEFAULT (8192*1024)
/* set this to zero to indicate we have no possible clients */
unsigned int client_max_connections;
int client_timeout;
size_t client_max_command_list_size;
size_t client_max_output_buffer_size;
void client_manager_init(void)
{
client_timeout = config_get_positive(CONF_CONN_TIMEOUT,
CLIENT_TIMEOUT_DEFAULT);
client_max_connections =
config_get_positive(CONF_MAX_CONN,
CLIENT_MAX_CONNECTIONS_DEFAULT);
client_max_command_list_size =
config_get_positive(CONF_MAX_COMMAND_LIST_SIZE,
CLIENT_MAX_COMMAND_LIST_DEFAULT / 1024)
* 1024;
client_max_output_buffer_size =
config_get_positive(CONF_MAX_OUTPUT_BUFFER_SIZE,
CLIENT_MAX_OUTPUT_BUFFER_SIZE_DEFAULT / 1024)
* 1024;
}
static void client_close_all(void)
{
while (!client_list_is_empty()) {
struct client *client = client_list_get_first();
client_close(client);
}
assert(client_list_is_empty());
}
void client_manager_deinit(void)
{
client_close_all();
client_max_connections = 0;
client_deinit_expire();
}

89
src/client_idle.c Normal file
View File

@@ -0,0 +1,89 @@
/*
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program 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.
*
* This program 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 this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h"
#include "client_internal.h"
#include "idle.h"
#include <assert.h>
/**
* Send "idle" response to this client.
*/
static void
client_idle_notify(struct client *client)
{
unsigned flags, i;
const char *const* idle_names;
assert(client->idle_waiting);
assert(client->idle_flags != 0);
flags = client->idle_flags;
client->idle_flags = 0;
client->idle_waiting = false;
idle_names = idle_get_names();
for (i = 0; idle_names[i]; ++i) {
if (flags & (1 << i) & client->idle_subscriptions)
client_printf(client, "changed: %s\n",
idle_names[i]);
}
client_puts(client, "OK\n");
g_timer_start(client->last_activity);
}
static void
client_idle_callback(gpointer data, gpointer user_data)
{
struct client *client = data;
unsigned flags = GPOINTER_TO_UINT(user_data);
if (client_is_expired(client))
return;
client->idle_flags |= flags;
if (client->idle_waiting
&& (client->idle_flags & client->idle_subscriptions)) {
client_idle_notify(client);
client_write_output(client);
}
}
void client_manager_idle_add(unsigned flags)
{
assert(flags != 0);
client_list_foreach(client_idle_callback, GUINT_TO_POINTER(flags));
}
bool client_idle_wait(struct client *client, unsigned flags)
{
assert(!client->idle_waiting);
client->idle_waiting = true;
client->idle_subscriptions = flags;
if (client->idle_flags & client->idle_subscriptions) {
client_idle_notify(client);
return true;
} else
return false;
}

145
src/client_internal.h Normal file
View File

@@ -0,0 +1,145 @@
/*
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program 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.
*
* This program 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 this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef MPD_CLIENT_INTERNAL_H
#define MPD_CLIENT_INTERNAL_H
#include "client.h"
#include "command.h"
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "client"
struct deferred_buffer {
size_t size;
char data[sizeof(long)];
};
struct client {
GIOChannel *channel;
guint source_id;
/** the buffer for reading lines from the #channel */
struct fifo_buffer *input;
unsigned permission;
/** the uid of the client process, or -1 if unknown */
int uid;
/**
* How long since the last activity from this client?
*/
GTimer *last_activity;
GSList *cmd_list; /* for when in list mode */
int cmd_list_OK; /* print OK after each command execution */
size_t cmd_list_size; /* mem cmd_list consumes */
GQueue *deferred_send; /* for output if client is slow */
size_t deferred_bytes; /* mem deferred_send consumes */
unsigned int num; /* client number */
char send_buf[16384];
size_t send_buf_used; /* bytes used this instance */
/** is this client waiting for an "idle" response? */
bool idle_waiting;
/** idle flags pending on this client, to be sent as soon as
the client enters "idle" */
unsigned idle_flags;
/** idle flags that the client wants to receive */
unsigned idle_subscriptions;
};
extern unsigned int client_max_connections;
extern int client_timeout;
extern size_t client_max_command_list_size;
extern size_t client_max_output_buffer_size;
bool
client_list_is_empty(void);
bool
client_list_is_full(void);
struct client *
client_list_get_first(void);
void
client_list_add(struct client *client);
void
client_list_foreach(GFunc func, gpointer user_data);
void
client_list_remove(struct client *client);
void
client_close(struct client *client);
static inline void
new_cmd_list_ptr(struct client *client, const char *s)
{
client->cmd_list = g_slist_prepend(client->cmd_list, g_strdup(s));
}
static inline void
free_cmd_list(GSList *list)
{
for (GSList *tmp = list; tmp != NULL; tmp = g_slist_next(tmp))
g_free(tmp->data);
g_slist_free(list);
}
void
client_set_expired(struct client *client);
/**
* Schedule an "expired" check for all clients: permanently delete
* clients which have been set "expired" with client_set_expired().
*/
void
client_schedule_expire(void);
/**
* Removes a scheduled "expired" check.
*/
void
client_deinit_expire(void);
enum command_return
client_read(struct client *client);
enum command_return
client_process_line(struct client *client, char *line);
void
client_write_deferred(struct client *client);
void
client_write_output(struct client *client);
gboolean
client_in_event(GIOChannel *source, GIOCondition condition,
gpointer data);
#endif

69
src/client_list.c Normal file
View File

@@ -0,0 +1,69 @@
/*
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program 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.
*
* This program 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 this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h"
#include "client_internal.h"
#include <assert.h>
static GList *clients;
static unsigned num_clients;
bool
client_list_is_empty(void)
{
return num_clients == 0;
}
bool
client_list_is_full(void)
{
return num_clients >= client_max_connections;
}
struct client *
client_list_get_first(void)
{
assert(clients != NULL);
return clients->data;
}
void
client_list_add(struct client *client)
{
clients = g_list_prepend(clients, client);
++num_clients;
}
void
client_list_foreach(GFunc func, gpointer user_data)
{
g_list_foreach(clients, func, user_data);
}
void
client_list_remove(struct client *client)
{
assert(num_clients > 0);
assert(clients != NULL);
clients = g_list_remove(clients, client);
--num_clients;
}

159
src/client_new.c Normal file
View File

@@ -0,0 +1,159 @@
/*
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program 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.
*
* This program 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 this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h"
#include "client_internal.h"
#include "fifo_buffer.h"
#include "socket_util.h"
#include "permission.h"
#include <assert.h>
#include <sys/types.h>
#ifdef WIN32
#include <winsock2.h>
#else
#include <sys/socket.h>
#endif
#include <unistd.h>
#ifdef HAVE_LIBWRAP
#include <tcpd.h>
#endif
#define LOG_LEVEL_SECURE G_LOG_LEVEL_INFO
static const char GREETING[] = "OK MPD " PROTOCOL_VERSION "\n";
void client_new(int fd, const struct sockaddr *sa, size_t sa_length, int uid)
{
static unsigned int next_client_num;
struct client *client;
char *remote;
assert(fd >= 0);
#ifdef HAVE_LIBWRAP
if (sa->sa_family != AF_UNIX) {
char *hostaddr = sockaddr_to_string(sa, sa_length, NULL);
const char *progname = g_get_prgname();
struct request_info req;
request_init(&req, RQ_FILE, fd, RQ_DAEMON, progname, 0);
fromhost(&req);
if (!hosts_access(&req)) {
/* tcp wrappers says no */
g_log(G_LOG_DOMAIN, LOG_LEVEL_SECURE,
"libwrap refused connection (libwrap=%s) from %s",
progname, hostaddr);
g_free(hostaddr);
close(fd);
return;
}
g_free(hostaddr);
}
#endif /* HAVE_WRAP */
if (client_list_is_full()) {
g_warning("Max Connections Reached!");
close(fd);
return;
}
client = g_new0(struct client, 1);
#ifndef G_OS_WIN32
client->channel = g_io_channel_unix_new(fd);
#else
client->channel = g_io_channel_win32_new_socket(fd);
#endif
/* GLib is responsible for closing the file descriptor */
g_io_channel_set_close_on_unref(client->channel, true);
/* NULL encoding means the stream is binary safe; the MPD
protocol is UTF-8 only, but we are doing this call anyway
to prevent GLib from messing around with the stream */
g_io_channel_set_encoding(client->channel, NULL, NULL);
/* we prefer to do buffering */
g_io_channel_set_buffered(client->channel, false);
client->source_id = g_io_add_watch(client->channel,
G_IO_IN|G_IO_ERR|G_IO_HUP,
client_in_event, client);
client->input = fifo_buffer_new(4096);
client->permission = getDefaultPermissions();
client->uid = uid;
client->last_activity = g_timer_new();
client->cmd_list = NULL;
client->cmd_list_OK = -1;
client->cmd_list_size = 0;
client->deferred_send = g_queue_new();
client->deferred_bytes = 0;
client->num = next_client_num++;
client->send_buf_used = 0;
(void)send(fd, GREETING, sizeof(GREETING) - 1, 0);
client_list_add(client);
remote = sockaddr_to_string(sa, sa_length, NULL);
g_log(G_LOG_DOMAIN, LOG_LEVEL_SECURE,
"[%u] opened from %s", client->num, remote);
g_free(remote);
}
static void
deferred_buffer_free(gpointer data, G_GNUC_UNUSED gpointer user_data)
{
struct deferred_buffer *buffer = data;
g_free(buffer);
}
void
client_close(struct client *client)
{
client_list_remove(client);
client_set_expired(client);
g_timer_destroy(client->last_activity);
if (client->cmd_list) {
free_cmd_list(client->cmd_list);
client->cmd_list = NULL;
}
g_queue_foreach(client->deferred_send, deferred_buffer_free, NULL);
g_queue_free(client->deferred_send);
fifo_buffer_free(client->input);
g_log(G_LOG_DOMAIN, LOG_LEVEL_SECURE,
"[%u] closed", client->num);
g_free(client);
}

146
src/client_process.c Normal file
View File

@@ -0,0 +1,146 @@
/*
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program 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.
*
* This program 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 this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h"
#include "client_internal.h"
#include <string.h>
#define CLIENT_LIST_MODE_BEGIN "command_list_begin"
#define CLIENT_LIST_OK_MODE_BEGIN "command_list_ok_begin"
#define CLIENT_LIST_MODE_END "command_list_end"
static enum command_return
client_process_command_list(struct client *client, bool list_ok, GSList *list)
{
enum command_return ret = COMMAND_RETURN_OK;
unsigned num = 0;
for (GSList *cur = list; cur != NULL; cur = g_slist_next(cur)) {
char *cmd = cur->data;
g_debug("command_process_list: process command \"%s\"",
cmd);
ret = command_process(client, num++, cmd);
g_debug("command_process_list: command returned %i", ret);
if (ret != COMMAND_RETURN_OK || client_is_expired(client))
break;
else if (list_ok)
client_puts(client, "list_OK\n");
}
return ret;
}
enum command_return
client_process_line(struct client *client, char *line)
{
enum command_return ret;
if (strcmp(line, "noidle") == 0) {
if (client->idle_waiting) {
/* send empty idle response and leave idle mode */
client->idle_waiting = false;
command_success(client);
client_write_output(client);
}
/* do nothing if the client wasn't idling: the client
has already received the full idle response from
client_idle_notify(), which he can now evaluate */
return COMMAND_RETURN_OK;
} else if (client->idle_waiting) {
/* during idle mode, clients must not send anything
except "noidle" */
g_warning("[%u] command \"%s\" during idle",
client->num, line);
return COMMAND_RETURN_CLOSE;
}
if (client->cmd_list_OK >= 0) {
if (strcmp(line, CLIENT_LIST_MODE_END) == 0) {
g_debug("[%u] process command list",
client->num);
/* for scalability reasons, we have prepended
each new command; now we have to reverse it
to restore the correct order */
client->cmd_list = g_slist_reverse(client->cmd_list);
ret = client_process_command_list(client,
client->cmd_list_OK,
client->cmd_list);
g_debug("[%u] process command "
"list returned %i", client->num, ret);
if (ret == COMMAND_RETURN_CLOSE ||
client_is_expired(client))
return COMMAND_RETURN_CLOSE;
if (ret == COMMAND_RETURN_OK)
command_success(client);
client_write_output(client);
free_cmd_list(client->cmd_list);
client->cmd_list = NULL;
client->cmd_list_OK = -1;
} else {
size_t len = strlen(line) + 1;
client->cmd_list_size += len;
if (client->cmd_list_size >
client_max_command_list_size) {
g_warning("[%u] command list size (%lu) "
"is larger than the max (%lu)",
client->num,
(unsigned long)client->cmd_list_size,
(unsigned long)client_max_command_list_size);
return COMMAND_RETURN_CLOSE;
}
new_cmd_list_ptr(client, line);
ret = COMMAND_RETURN_OK;
}
} else {
if (strcmp(line, CLIENT_LIST_MODE_BEGIN) == 0) {
client->cmd_list_OK = 0;
ret = COMMAND_RETURN_OK;
} else if (strcmp(line, CLIENT_LIST_OK_MODE_BEGIN) == 0) {
client->cmd_list_OK = 1;
ret = COMMAND_RETURN_OK;
} else {
g_debug("[%u] process command \"%s\"",
client->num, line);
ret = command_process(client, 0, line);
g_debug("[%u] command returned %i",
client->num, ret);
if (ret == COMMAND_RETURN_CLOSE ||
client_is_expired(client))
return COMMAND_RETURN_CLOSE;
if (ret == COMMAND_RETURN_OK)
command_success(client);
client_write_output(client);
}
}
return ret;
}

113
src/client_read.c Normal file
View File

@@ -0,0 +1,113 @@
/*
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program 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.
*
* This program 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 this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h"
#include "client_internal.h"
#include "fifo_buffer.h"
#include <assert.h>
#include <string.h>
static char *
client_read_line(struct client *client)
{
const char *p, *newline;
size_t length;
char *line;
p = fifo_buffer_read(client->input, &length);
if (p == NULL)
return NULL;
newline = memchr(p, '\n', length);
if (newline == NULL)
return NULL;
line = g_strndup(p, newline - p);
fifo_buffer_consume(client->input, newline - p + 1);
return g_strchomp(line);
}
static enum command_return
client_input_received(struct client *client, size_t bytesRead)
{
char *line;
fifo_buffer_append(client->input, bytesRead);
/* process all lines */
while ((line = client_read_line(client)) != NULL) {
enum command_return ret = client_process_line(client, line);
g_free(line);
if (ret == COMMAND_RETURN_KILL ||
ret == COMMAND_RETURN_CLOSE)
return ret;
if (client_is_expired(client))
return COMMAND_RETURN_CLOSE;
}
return COMMAND_RETURN_OK;
}
enum command_return
client_read(struct client *client)
{
char *p;
size_t max_length;
GError *error = NULL;
GIOStatus status;
gsize bytes_read;
assert(client != NULL);
assert(client->channel != NULL);
p = fifo_buffer_write(client->input, &max_length);
if (p == NULL) {
g_warning("[%u] buffer overflow", client->num);
return COMMAND_RETURN_CLOSE;
}
status = g_io_channel_read_chars(client->channel, p, max_length,
&bytes_read, &error);
switch (status) {
case G_IO_STATUS_NORMAL:
return client_input_received(client, bytes_read);
case G_IO_STATUS_AGAIN:
/* try again later, after select() */
return COMMAND_RETURN_OK;
case G_IO_STATUS_EOF:
/* peer disconnected */
return COMMAND_RETURN_CLOSE;
case G_IO_STATUS_ERROR:
/* I/O error */
g_warning("failed to read from client %d: %s",
client->num, error->message);
g_error_free(error);
return COMMAND_RETURN_CLOSE;
}
/* unreachable */
return COMMAND_RETURN_CLOSE;
}

284
src/client_write.c Normal file
View File

@@ -0,0 +1,284 @@
/*
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program 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.
*
* This program 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 this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h"
#include "client_internal.h"
#include <assert.h>
#include <string.h>
#include <stdio.h>
static size_t
client_write_deferred_buffer(struct client *client,
const struct deferred_buffer *buffer)
{
GError *error = NULL;
GIOStatus status;
gsize bytes_written;
assert(client != NULL);
assert(client->channel != NULL);
assert(buffer != NULL);
status = g_io_channel_write_chars
(client->channel, buffer->data, buffer->size,
&bytes_written, &error);
switch (status) {
case G_IO_STATUS_NORMAL:
return bytes_written;
case G_IO_STATUS_AGAIN:
return 0;
case G_IO_STATUS_EOF:
/* client has disconnected */
client_set_expired(client);
return 0;
case G_IO_STATUS_ERROR:
/* I/O error */
client_set_expired(client);
g_warning("failed to flush buffer for %i: %s",
client->num, error->message);
g_error_free(error);
return 0;
}
/* unreachable */
return 0;
}
void
client_write_deferred(struct client *client)
{
size_t ret;
while (!g_queue_is_empty(client->deferred_send)) {
struct deferred_buffer *buf =
g_queue_peek_head(client->deferred_send);
assert(buf->size > 0);
assert(buf->size <= client->deferred_bytes);
ret = client_write_deferred_buffer(client, buf);
if (ret == 0)
break;
if (ret < buf->size) {
assert(client->deferred_bytes >= (size_t)ret);
client->deferred_bytes -= ret;
buf->size -= ret;
memmove(buf->data, buf->data + ret, buf->size);
break;
} else {
size_t decr = sizeof(*buf) -
sizeof(buf->data) + buf->size;
assert(client->deferred_bytes >= decr);
client->deferred_bytes -= decr;
g_free(buf);
g_queue_pop_head(client->deferred_send);
}
g_timer_start(client->last_activity);
}
if (g_queue_is_empty(client->deferred_send)) {
g_debug("[%u] buffer empty %lu", client->num,
(unsigned long)client->deferred_bytes);
assert(client->deferred_bytes == 0);
}
}
static void client_defer_output(struct client *client,
const void *data, size_t length)
{
size_t alloc;
struct deferred_buffer *buf;
assert(length > 0);
alloc = sizeof(*buf) - sizeof(buf->data) + length;
client->deferred_bytes += alloc;
if (client->deferred_bytes > client_max_output_buffer_size) {
g_warning("[%u] output buffer size (%lu) is "
"larger than the max (%lu)",
client->num,
(unsigned long)client->deferred_bytes,
(unsigned long)client_max_output_buffer_size);
/* cause client to close */
client_set_expired(client);
return;
}
buf = g_malloc(alloc);
buf->size = length;
memcpy(buf->data, data, length);
g_queue_push_tail(client->deferred_send, buf);
}
static void client_write_direct(struct client *client,
const char *data, size_t length)
{
GError *error = NULL;
GIOStatus status;
gsize bytes_written;
assert(client != NULL);
assert(client->channel != NULL);
assert(data != NULL);
assert(length > 0);
assert(g_queue_is_empty(client->deferred_send));
status = g_io_channel_write_chars(client->channel, data, length,
&bytes_written, &error);
switch (status) {
case G_IO_STATUS_NORMAL:
case G_IO_STATUS_AGAIN:
break;
case G_IO_STATUS_EOF:
/* client has disconnected */
client_set_expired(client);
return;
case G_IO_STATUS_ERROR:
/* I/O error */
client_set_expired(client);
g_warning("failed to write to %i: %s",
client->num, error->message);
g_error_free(error);
return;
}
if (bytes_written < length)
client_defer_output(client, data + bytes_written,
length - bytes_written);
if (!g_queue_is_empty(client->deferred_send))
g_debug("[%u] buffer created", client->num);
}
void
client_write_output(struct client *client)
{
if (client_is_expired(client) || !client->send_buf_used)
return;
if (!g_queue_is_empty(client->deferred_send)) {
client_defer_output(client, client->send_buf,
client->send_buf_used);
if (client_is_expired(client))
return;
/* try to flush the deferred buffers now; the current
server command may take too long to finish, and
meanwhile try to feed output to the client,
otherwise it will time out. One reason why
deferring is slow might be that currently each
client_write() allocates a new deferred buffer.
This should be optimized after MPD 0.14. */
client_write_deferred(client);
} else
client_write_direct(client, client->send_buf,
client->send_buf_used);
client->send_buf_used = 0;
}
/**
* Write a block of data to the client.
*/
static void client_write(struct client *client, const char *buffer, size_t buflen)
{
/* if the client is going to be closed, do nothing */
if (client_is_expired(client))
return;
while (buflen > 0 && !client_is_expired(client)) {
size_t copylen;
assert(client->send_buf_used < sizeof(client->send_buf));
copylen = sizeof(client->send_buf) - client->send_buf_used;
if (copylen > buflen)
copylen = buflen;
memcpy(client->send_buf + client->send_buf_used, buffer,
copylen);
buflen -= copylen;
client->send_buf_used += copylen;
buffer += copylen;
if (client->send_buf_used >= sizeof(client->send_buf))
client_write_output(client);
}
}
void client_puts(struct client *client, const char *s)
{
client_write(client, s, strlen(s));
}
void client_vprintf(struct client *client, const char *fmt, va_list args)
{
#ifndef G_OS_WIN32
va_list tmp;
int length;
char *buffer;
va_copy(tmp, args);
length = vsnprintf(NULL, 0, fmt, tmp);
va_end(tmp);
if (length <= 0)
/* wtf.. */
return;
buffer = g_malloc(length + 1);
vsnprintf(buffer, length + 1, fmt, args);
client_write(client, buffer, length);
g_free(buffer);
#else
/* On mingw32, snprintf() expects a 64 bit integer instead of
a "long int" for "%li". This is not consistent with our
expectation, so we're using plain sprintf() here, hoping
the static buffer is large enough. Sorry for this hack,
but WIN32 development is so painful, I'm not in the mood to
do it properly now. */
static char buffer[4096];
vsprintf(buffer, fmt, args);
client_write(client, buffer, strlen(buffer));
#endif
}
G_GNUC_PRINTF(2, 3) void client_printf(struct client *client, const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
client_vprintf(client, fmt, args);
va_end(args);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2003-2009 The Music Player Daemon Project
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,14 +17,20 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h"
#include "cmdline.h"
#include "path.h"
#include "log.h"
#include "conf.h"
#include "decoder_list.h"
#include "config.h"
#include "decoder_plugin.h"
#include "output_list.h"
#include "ls.h"
#include "mpd_error.h"
#ifdef ENABLE_ENCODER
#include "encoder_list.h"
#endif
#ifdef ENABLE_ARCHIVE
#include "archive_list.h"
@@ -35,9 +41,37 @@
#include <stdio.h>
#include <stdlib.h>
#define SYSTEM_CONFIG_FILE_LOCATION "/etc/mpd.conf"
#ifdef G_OS_WIN32
#define CONFIG_FILE_LOCATION "\\mpd\\mpd.conf"
#else /* G_OS_WIN32 */
#define USER_CONFIG_FILE_LOCATION1 ".mpdconf"
#define USER_CONFIG_FILE_LOCATION2 ".mpd/mpd.conf"
#endif
static GQuark
cmdline_quark(void)
{
return g_quark_from_static_string("cmdline");
}
static void
print_all_decoders(FILE *fp)
{
for (unsigned i = 0; decoder_plugins[i] != NULL; ++i) {
const struct decoder_plugin *plugin = decoder_plugins[i];
const char *const*suffixes;
fprintf(fp, "[%s]", plugin->name);
for (suffixes = plugin->suffixes;
suffixes != NULL && *suffixes != NULL;
++suffixes) {
fprintf(fp, " %s", *suffixes);
}
fprintf(fp, "\n");
}
}
G_GNUC_NORETURN
static void version(void)
@@ -45,19 +79,25 @@ static void version(void)
puts(PACKAGE " (MPD: Music Player Daemon) " VERSION " \n"
"\n"
"Copyright (C) 2003-2007 Warren Dukes <warren.dukes@gmail.com>\n"
"Copyright (C) 2008 Max Kellermann <max@duempel.org>\n"
"Copyright (C) 2008-2010 Max Kellermann <max@duempel.org>\n"
"This is free software; see the source for copying conditions. There is NO\n"
"warranty; not even MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
"\n"
"Supported decoders:\n");
decoder_plugin_init_all();
decoder_plugin_print_all_decoders(stdout);
print_all_decoders(stdout);
puts("\n"
"Supported outputs:\n");
audio_output_plugin_print_all_types(stdout);
#ifdef ENABLE_ENCODER
puts("\n"
"Supported encoders:\n");
encoder_plugin_print_all_types(stdout);
#endif
#ifdef ENABLE_ARCHIVE
puts("\n"
"Supported archives:\n");
@@ -72,31 +112,29 @@ static void version(void)
exit(EXIT_SUCCESS);
}
#if GLIB_CHECK_VERSION(2,12,0)
static const char *summary =
"Music Player Daemon - a daemon for playing music.";
#endif
void parseOptions(int argc, char **argv, Options *options)
bool
parse_cmdline(int argc, char **argv, struct options *options,
GError **error_r)
{
GError *error = NULL;
GOptionContext *context;
bool ret;
static gboolean option_version,
option_create_db, option_no_create_db, option_no_daemon,
option_no_daemon,
option_no_config;
const GOptionEntry entries[] = {
{ "create-db", 0, 0, G_OPTION_ARG_NONE, &option_create_db,
"force (re)creation of database", NULL },
{ "kill", 0, 0, G_OPTION_ARG_NONE, &options->kill,
"kill the currently running mpd session", NULL },
{ "no-config", 0, 0, G_OPTION_ARG_NONE, &option_no_config,
"don't read from config", NULL },
{ "no-create-db", 0, 0, G_OPTION_ARG_NONE, &option_no_create_db,
"don't create database, even if it doesn't exist", NULL },
{ "no-daemon", 0, 0, G_OPTION_ARG_NONE, &option_no_daemon,
"don't detach from console", NULL },
{ "stdout", 0, 0, G_OPTION_ARG_NONE, &options->stdOutput,
{ "stdout", 0, 0, G_OPTION_ARG_NONE, &options->log_stderr,
NULL, NULL },
{ "stderr", 0, 0, G_OPTION_ARG_NONE, &options->log_stderr,
"print messages to stderr", NULL },
{ "verbose", 'v', 0, G_OPTION_ARG_NONE, &options->verbose,
"verbose logging", NULL },
@@ -107,24 +145,19 @@ void parseOptions(int argc, char **argv, Options *options)
options->kill = false;
options->daemon = true;
options->stdOutput = false;
options->log_stderr = false;
options->verbose = false;
options->createDB = 0;
context = g_option_context_new("[path/to/mpd.conf]");
g_option_context_add_main_entries(context, entries, NULL);
#if GLIB_CHECK_VERSION(2,12,0)
g_option_context_set_summary(context, summary);
#endif
ret = g_option_context_parse(context, &argc, &argv, &error);
g_option_context_free(context);
if (!ret) {
g_error("option parsing failed: %s\n", error->message);
exit(1);
}
if (!ret)
MPD_ERROR("option parsing failed: %s\n", error->message);
if (option_version)
version();
@@ -133,39 +166,71 @@ void parseOptions(int argc, char **argv, Options *options)
parser can use it already */
log_early_init(options->verbose);
if (option_create_db && option_no_create_db)
g_error("Cannot use both --create-db and --no-create-db\n");
if (option_no_create_db)
options->createDB = -1;
else if (option_create_db)
options->createDB = 1;
options->daemon = !option_no_daemon;
if (option_no_config) {
g_debug("Ignoring config, using daemon defaults\n");
return true;
} else if (argc <= 1) {
/* default configuration file path */
char *path1;
char *path2;
#ifdef G_OS_WIN32
path1 = g_build_filename(g_get_user_config_dir(),
CONFIG_FILE_LOCATION, NULL);
if (g_file_test(path1, G_FILE_TEST_IS_REGULAR))
ret = config_read_file(path1, error_r);
else {
int i = 0;
char *system_path = NULL;
const char * const *system_config_dirs;
system_config_dirs = g_get_system_config_dirs();
while(system_config_dirs[i] != NULL) {
system_path = g_build_filename(system_config_dirs[i],
CONFIG_FILE_LOCATION,
NULL);
if(g_file_test(system_path,
G_FILE_TEST_IS_REGULAR)) {
ret = config_read_file(system_path,error_r);
g_free(system_path);
g_free(&system_config_dirs);
break;
}
++i;;
}
g_free(system_path);
g_free(&system_config_dirs);
}
#else /* G_OS_WIN32 */
char *path2;
path1 = g_build_filename(g_get_home_dir(),
USER_CONFIG_FILE_LOCATION1, NULL);
path2 = g_build_filename(g_get_home_dir(),
USER_CONFIG_FILE_LOCATION2, NULL);
if (g_file_test(path1, G_FILE_TEST_IS_REGULAR))
config_read_file(path1);
ret = config_read_file(path1, error_r);
else if (g_file_test(path2, G_FILE_TEST_IS_REGULAR))
config_read_file(path2);
ret = config_read_file(path2, error_r);
else if (g_file_test(SYSTEM_CONFIG_FILE_LOCATION,
G_FILE_TEST_IS_REGULAR))
config_read_file(SYSTEM_CONFIG_FILE_LOCATION);
ret = config_read_file(SYSTEM_CONFIG_FILE_LOCATION,
error_r);
#endif
g_free(path1);
#ifndef G_OS_WIN32
g_free(path2);
#endif
return ret;
} else if (argc == 2) {
/* specified configuration file */
config_read_file(argv[1]);
} else
g_error("too many arguments");
return config_read_file(argv[1], error_r);
} else {
g_set_error(error_r, cmdline_quark(), 0,
"too many arguments");
return false;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2003-2009 The Music Player Daemon Project
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -22,14 +22,17 @@
#include <glib.h>
typedef struct _Options {
#include <stdbool.h>
struct options {
gboolean kill;
gboolean daemon;
gboolean stdOutput;
gboolean log_stderr;
gboolean verbose;
int createDB;
} Options;
};
void parseOptions(int argc, char **argv, Options *options);
bool
parse_cmdline(int argc, char **argv, struct options *options,
GError **error_r);
#endif

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2003-2009 The Music Player Daemon Project
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,14 +17,17 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h"
#include "command.h"
#include "player_control.h"
#include "playlist.h"
#include "playlist_print.h"
#include "playlist_save.h"
#include "playlist_queue.h"
#include "queue_print.h"
#include "ls.h"
#include "uri.h"
#include "decoder_print.h"
#include "directory.h"
#include "directory_print.h"
#include "database.h"
@@ -32,7 +35,7 @@
#include "volume.h"
#include "stats.h"
#include "permission.h"
#include "buffer2array.h"
#include "tokenizer.h"
#include "stored_playlist.h"
#include "ack.h"
#include "output_command.h"
@@ -43,8 +46,8 @@
#include "client.h"
#include "tag_print.h"
#include "path.h"
#include "replay_gain_config.h"
#include "idle.h"
#include "config.h"
#ifdef ENABLE_SQLITE
#include "sticker.h"
@@ -58,7 +61,6 @@
#include <stdlib.h>
#include <errno.h>
#define COMMAND_STATUS_VOLUME "volume"
#define COMMAND_STATUS_STATE "state"
#define COMMAND_STATUS_REPEAT "repeat"
#define COMMAND_STATUS_SINGLE "single"
@@ -74,6 +76,8 @@
#define COMMAND_STATUS_BITRATE "bitrate"
#define COMMAND_STATUS_ERROR "error"
#define COMMAND_STATUS_CROSSFADE "xfade"
#define COMMAND_STATUS_MIXRAMPDB "mixrampdb"
#define COMMAND_STATUS_MIXRAMPDELAY "mixrampdelay"
#define COMMAND_STATUS_AUDIO "audio"
#define COMMAND_STATUS_UPDATING_DB "updating_db"
@@ -166,8 +170,8 @@ check_int(struct client *client, int *value_r,
return false;
}
#if LONG_MAX > INT_MAX
if (value < INT_MIN || value > INT_MAX) {
#if G_MAXLONG > G_MAXINT
if (value < G_MININT || value > G_MAXINT) {
command_error(client, ACK_ERROR_ARG,
"Number too large: %s", s);
return false;
@@ -198,7 +202,7 @@ check_range(struct client *client, unsigned *value_r1, unsigned *value_r2,
/* compatibility with older MPD versions: specifying
"-1" makes MPD display the whole list */
*value_r1 = 0;
*value_r2 = UINT_MAX;
*value_r2 = G_MAXUINT;
return true;
}
@@ -208,8 +212,8 @@ check_range(struct client *client, unsigned *value_r1, unsigned *value_r2,
return false;
}
#if LONG_MAX > UINT_MAX
if (value > UINT_MAX) {
#if G_MAXLONG > G_MAXUINT
if (value > G_MAXUINT) {
command_error(client, ACK_ERROR_ARG,
"Number too large: %s", s);
return false;
@@ -220,7 +224,7 @@ check_range(struct client *client, unsigned *value_r1, unsigned *value_r2,
if (*test == ':') {
value = strtol(++test, &test2, 10);
if (*test2 != '\0' || test == test2) {
if (*test2 != '\0') {
va_list args;
va_start(args, fmt);
command_error_v(client, ACK_ERROR_ARG, fmt, args);
@@ -228,14 +232,17 @@ check_range(struct client *client, unsigned *value_r1, unsigned *value_r2,
return false;
}
if (test == test2)
value = G_MAXUINT;
if (value < 0) {
command_error(client, ACK_ERROR_ARG,
"Number is negative: %s", s);
return false;
}
#if LONG_MAX > UINT_MAX
if (value > UINT_MAX) {
#if G_MAXLONG > G_MAXUINT
if (value > G_MAXUINT) {
command_error(client, ACK_ERROR_ARG,
"Number too large: %s", s);
return false;
@@ -262,7 +269,7 @@ check_unsigned(struct client *client, unsigned *value_r, const char *s)
return false;
}
if (value > UINT_MAX) {
if (value > G_MAXUINT) {
command_error(client, ACK_ERROR_ARG,
"Number too large: %s", s);
return false;
@@ -289,6 +296,23 @@ check_bool(struct client *client, bool *value_r, const char *s)
return true;
}
static bool
check_float(struct client *client, float *value_r, const char *s)
{
float value;
char *endptr;
value = strtof(s, &endptr);
if (*endptr != 0 && endptr == s) {
command_error(client, ACK_ERROR_ARG,
"Float expected: %s", s);
return false;
}
*value_r = value;
return true;
}
static enum command_return
print_playlist_result(struct client *client,
enum playlist_result result)
@@ -363,10 +387,12 @@ print_spl_list(struct client *client, GPtrArray *list)
client_printf(client, "playlist: %s\n", playlist->name);
t = playlist->mtime;
strftime(timestamp, sizeof(timestamp), "%FT%TZ",
#ifdef WIN32
strftime(timestamp, sizeof(timestamp),
#ifdef G_OS_WIN32
"%Y-%m-%dT%H:%M:%SZ",
gmtime(&t)
#else
"%FT%TZ",
gmtime_r(&t, &tm)
#endif
);
@@ -384,6 +410,14 @@ handle_urlhandlers(struct client *client,
return COMMAND_RETURN_OK;
}
static enum command_return
handle_decoders(struct client *client,
G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
{
decoder_list_print(client);
return COMMAND_RETURN_OK;
}
static enum command_return
handle_tagtypes(struct client *client,
G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
@@ -400,7 +434,7 @@ handle_play(struct client *client, int argc, char *argv[])
if (argc == 2 && !check_int(client, &song, argv[1], need_positive))
return COMMAND_RETURN_ERROR;
result = playPlaylist(&g_playlist, song);
result = playlist_play(&g_playlist, song);
return print_playlist_result(client, result);
}
@@ -413,7 +447,7 @@ handle_playid(struct client *client, int argc, char *argv[])
if (argc == 2 && !check_int(client, &id, argv[1], need_positive))
return COMMAND_RETURN_ERROR;
result = playPlaylistById(&g_playlist, id);
result = playlist_play_id(&g_playlist, id);
return print_playlist_result(client, result);
}
@@ -421,7 +455,7 @@ static enum command_return
handle_stop(G_GNUC_UNUSED struct client *client,
G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
{
stopPlaylist(&g_playlist);
playlist_stop(&g_playlist);
return COMMAND_RETURN_OK;
}
@@ -441,11 +475,11 @@ handle_pause(struct client *client,
bool pause_flag;
if (!check_bool(client, &pause_flag, argv[1]))
return COMMAND_RETURN_ERROR;
playerSetPause(pause_flag);
return COMMAND_RETURN_OK;
}
playerPause();
pc_set_pause(pause_flag);
} else
pc_pause();
return COMMAND_RETURN_OK;
}
@@ -454,10 +488,14 @@ handle_status(struct client *client,
G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
{
const char *state = NULL;
struct player_status player_status;
int updateJobId;
char *error;
int song;
switch (getPlayerState()) {
pc_get_status(&player_status);
switch (player_status.state) {
case PLAYER_STATE_STOP:
state = "stop";
break;
@@ -470,7 +508,7 @@ handle_status(struct client *client,
}
client_printf(client,
COMMAND_STATUS_VOLUME ": %i\n"
"volume: %i\n"
COMMAND_STATUS_REPEAT ": %i\n"
COMMAND_STATUS_RANDOM ": %i\n"
COMMAND_STATUS_SINGLE ": %i\n"
@@ -478,34 +516,43 @@ handle_status(struct client *client,
COMMAND_STATUS_PLAYLIST ": %li\n"
COMMAND_STATUS_PLAYLIST_LENGTH ": %i\n"
COMMAND_STATUS_CROSSFADE ": %i\n"
COMMAND_STATUS_MIXRAMPDB ": %f\n"
COMMAND_STATUS_MIXRAMPDELAY ": %f\n"
COMMAND_STATUS_STATE ": %s\n",
volume_level_get(),
getPlaylistRepeatStatus(&g_playlist),
getPlaylistRandomStatus(&g_playlist),
getPlaylistSingleStatus(&g_playlist),
getPlaylistConsumeStatus(&g_playlist),
getPlaylistVersion(&g_playlist),
getPlaylistLength(&g_playlist),
(int)(getPlayerCrossFade() + 0.5),
playlist_get_repeat(&g_playlist),
playlist_get_random(&g_playlist),
playlist_get_single(&g_playlist),
playlist_get_consume(&g_playlist),
playlist_get_version(&g_playlist),
playlist_get_length(&g_playlist),
(int)(pc_get_cross_fade() + 0.5),
pc_get_mixramp_db(),
pc_get_mixramp_delay(),
state);
song = getPlaylistCurrentSong(&g_playlist);
song = playlist_get_current_song(&g_playlist);
if (song >= 0) {
client_printf(client,
COMMAND_STATUS_SONG ": %i\n"
COMMAND_STATUS_SONGID ": %u\n",
song, getPlaylistSongId(&g_playlist, song));
song, playlist_get_song_id(&g_playlist, song));
}
if (getPlayerState() != PLAYER_STATE_STOP) {
const struct audio_format *af = player_get_audio_format();
if (player_status.state != PLAYER_STATE_STOP) {
struct audio_format_string af_string;
client_printf(client,
COMMAND_STATUS_TIME ": %i:%i\n"
COMMAND_STATUS_BITRATE ": %li\n"
COMMAND_STATUS_AUDIO ": %u:%u:%u\n",
getPlayerElapsedTime(), getPlayerTotalTime(),
getPlayerBitRate(),
af->sample_rate, af->bits, af->channels);
"elapsed: %1.3f\n"
COMMAND_STATUS_BITRATE ": %u\n"
COMMAND_STATUS_AUDIO ": %s\n",
(int)(player_status.elapsed_time + 0.5),
(int)(player_status.total_time + 0.5),
player_status.elapsed_time,
player_status.bit_rate,
audio_format_to_string(&player_status.audio_format,
&af_string));
}
if ((updateJobId = isUpdatingDB())) {
@@ -514,18 +561,20 @@ handle_status(struct client *client,
updateJobId);
}
if (getPlayerError() != PLAYER_ERROR_NOERROR) {
error = pc_get_error_message();
if (error != NULL) {
client_printf(client,
COMMAND_STATUS_ERROR ": %s\n",
getPlayerErrorStr());
error);
g_free(error);
}
song = getPlaylistNextSong(&g_playlist);
song = playlist_get_next_song(&g_playlist);
if (song >= 0) {
client_printf(client,
COMMAND_STATUS_NEXTSONG ": %i\n"
COMMAND_STATUS_NEXTSONGID ": %u\n",
song, getPlaylistSongId(&g_playlist, song));
song, playlist_get_song_id(&g_playlist, song));
}
return COMMAND_RETURN_OK;
@@ -569,7 +618,7 @@ handle_add(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
return COMMAND_RETURN_ERROR;
}
result = addToPlaylist(&g_playlist, uri, NULL);
result = playlist_append_uri(&g_playlist, uri, NULL);
return print_playlist_result(client, result);
}
@@ -605,7 +654,7 @@ handle_addid(struct client *client, int argc, char *argv[])
return COMMAND_RETURN_ERROR;
}
result = addToPlaylist(&g_playlist, uri, &added_id);
result = playlist_append_uri(&g_playlist, uri, &added_id);
}
if (result != PLAYLIST_RESULT_SUCCESS)
@@ -615,11 +664,11 @@ handle_addid(struct client *client, int argc, char *argv[])
int to;
if (!check_int(client, &to, argv[2], check_integer, argv[2]))
return COMMAND_RETURN_ERROR;
result = moveSongInPlaylistById(&g_playlist, added_id, to);
result = playlist_move_id(&g_playlist, added_id, to);
if (result != PLAYLIST_RESULT_SUCCESS) {
enum command_return ret =
print_playlist_result(client, result);
deleteFromPlaylistById(&g_playlist, added_id);
playlist_delete_id(&g_playlist, added_id);
return ret;
}
}
@@ -631,13 +680,13 @@ handle_addid(struct client *client, int argc, char *argv[])
static enum command_return
handle_delete(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
{
int song;
unsigned start, end;
enum playlist_result result;
if (!check_int(client, &song, argv[1], need_positive))
if (!check_range(client, &start, &end, argv[1], need_range))
return COMMAND_RETURN_ERROR;
result = deleteFromPlaylist(&g_playlist, song);
result = playlist_delete_range(&g_playlist, start, end);
return print_playlist_result(client, result);
}
@@ -650,7 +699,7 @@ handle_deleteid(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
if (!check_int(client, &id, argv[1], need_positive))
return COMMAND_RETURN_ERROR;
result = deleteFromPlaylistById(&g_playlist, id);
result = playlist_delete_id(&g_playlist, id);
return print_playlist_result(client, result);
}
@@ -671,7 +720,7 @@ handle_shuffle(G_GNUC_UNUSED struct client *client,
argv[1], need_range))
return COMMAND_RETURN_ERROR;
shufflePlaylist(&g_playlist, start, end);
playlist_shuffle(&g_playlist, start, end);
return COMMAND_RETURN_OK;
}
@@ -679,7 +728,7 @@ static enum command_return
handle_clear(G_GNUC_UNUSED struct client *client,
G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
{
clearPlaylist(&g_playlist);
playlist_clear(&g_playlist);
return COMMAND_RETURN_OK;
}
@@ -698,6 +747,10 @@ handle_load(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
{
enum playlist_result result;
result = playlist_open_into_queue(argv[1], &g_playlist);
if (result != PLAYLIST_RESULT_NO_SUCH_LIST)
return result;
result = playlist_load_spl(&g_playlist, argv[1]);
return print_playlist_result(client, result);
}
@@ -705,6 +758,9 @@ handle_load(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
static enum command_return
handle_listplaylist(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
{
if (playlist_file_print(client, argv[1], false))
return COMMAND_RETURN_OK;
bool ret;
ret = spl_print(client, argv[1], false);
@@ -720,6 +776,9 @@ static enum command_return
handle_listplaylistinfo(struct client *client,
G_GNUC_UNUSED int argc, char *argv[])
{
if (playlist_file_print(client, argv[1], true))
return COMMAND_RETURN_OK;
bool ret;
ret = spl_print(client, argv[1], true);
@@ -808,7 +867,7 @@ handle_plchangesposid(struct client *client, G_GNUC_UNUSED int argc, char *argv[
static enum command_return
handle_playlistinfo(struct client *client, int argc, char *argv[])
{
unsigned start = 0, end = UINT_MAX;
unsigned start = 0, end = G_MAXUINT;
bool ret;
if (argc == 2 && !check_range(client, &start, &end,
@@ -837,7 +896,7 @@ handle_playlistid(struct client *client, int argc, char *argv[])
return print_playlist_result(client,
PLAYLIST_RESULT_NO_SUCH_SONG);
} else {
playlist_print_info(client, &g_playlist, 0, UINT_MAX);
playlist_print_info(client, &g_playlist, 0, G_MAXUINT);
}
return COMMAND_RETURN_OK;
@@ -868,6 +927,30 @@ handle_find(struct client *client, int argc, char *argv[])
return ret;
}
static enum command_return
handle_findadd(struct client *client, int argc, char *argv[])
{
int ret;
struct locate_item_list *list =
locate_item_list_parse(argv + 1, argc - 1);
if (list == NULL || list->length == 0) {
if (list != NULL)
locate_item_list_free(list);
command_error(client, ACK_ERROR_ARG, "incorrect arguments");
return COMMAND_RETURN_ERROR;
}
ret = findAddIn(client, NULL, list);
if (ret == -1)
command_error(client, ACK_ERROR_NO_EXIST,
"directory or file not found");
locate_item_list_free(list);
return ret;
}
static enum command_return
handle_search(struct client *client, int argc, char *argv[])
{
@@ -993,14 +1076,52 @@ handle_playlistmove(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
static enum command_return
handle_update(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
{
char *path = NULL;
const char *path = NULL;
unsigned ret;
assert(argc <= 2);
if (argc == 2)
path = g_strdup(argv[1]);
if (argc == 2) {
path = argv[1];
ret = directory_update_init(path);
if (*path == 0 || strcmp(path, "/") == 0)
/* backwards compatibility with MPD 0.15 */
path = NULL;
else if (!uri_safe_local(path)) {
command_error(client, ACK_ERROR_ARG,
"Malformed path");
return COMMAND_RETURN_ERROR;
}
}
ret = update_enqueue(path, false);
if (ret > 0) {
client_printf(client, "updating_db: %i\n", ret);
return COMMAND_RETURN_OK;
} else {
command_error(client, ACK_ERROR_UPDATE_ALREADY,
"already updating");
return COMMAND_RETURN_ERROR;
}
}
static enum command_return
handle_rescan(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
{
const char *path = NULL;
unsigned ret;
assert(argc <= 2);
if (argc == 2) {
path = argv[1];
if (!uri_safe_local(path)) {
command_error(client, ACK_ERROR_ARG,
"Malformed path");
return COMMAND_RETURN_ERROR;
}
}
ret = update_enqueue(path, true);
if (ret > 0) {
client_printf(client, "updating_db: %i\n", ret);
return COMMAND_RETURN_OK;
@@ -1020,7 +1141,7 @@ handle_next(G_GNUC_UNUSED struct client *client,
int single = g_playlist.queue.single;
g_playlist.queue.single = false;
nextSongInPlaylist(&g_playlist);
playlist_next(&g_playlist);
g_playlist.queue.single = single;
return COMMAND_RETURN_OK;
@@ -1030,7 +1151,7 @@ static enum command_return
handle_previous(G_GNUC_UNUSED struct client *client,
G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
{
previousSongInPlaylist(&g_playlist);
playlist_previous(&g_playlist);
return COMMAND_RETURN_OK;
}
@@ -1051,25 +1172,6 @@ handle_listall(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
return ret;
}
static enum command_return
handle_volume(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
{
int change;
bool success;
if (!check_int(client, &change, argv[1], need_integer))
return COMMAND_RETURN_ERROR;
success = volume_level_change(change, true);
if (!success) {
command_error(client, ACK_ERROR_SYSTEM,
"problems setting volume");
return COMMAND_RETURN_ERROR;
}
return COMMAND_RETURN_OK;
}
static enum command_return
handle_setvol(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
{
@@ -1079,7 +1181,12 @@ handle_setvol(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
if (!check_int(client, &level, argv[1], need_integer))
return COMMAND_RETURN_ERROR;
success = volume_level_change(level, 0);
if (level < 0 || level > 100) {
command_error(client, ACK_ERROR_ARG, "Invalid volume value");
return COMMAND_RETURN_ERROR;
}
success = volume_level_change(level);
if (!success) {
command_error(client, ACK_ERROR_SYSTEM,
"problems setting volume");
@@ -1103,7 +1210,7 @@ handle_repeat(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
return COMMAND_RETURN_ERROR;
}
setPlaylistRepeatStatus(&g_playlist, status);
playlist_set_repeat(&g_playlist, status);
return COMMAND_RETURN_OK;
}
@@ -1121,7 +1228,7 @@ handle_single(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
return COMMAND_RETURN_ERROR;
}
setPlaylistSingleStatus(&g_playlist, status);
playlist_set_single(&g_playlist, status);
return COMMAND_RETURN_OK;
}
@@ -1139,7 +1246,7 @@ handle_consume(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
return COMMAND_RETURN_ERROR;
}
setPlaylistConsumeStatus(&g_playlist, status);
playlist_set_consume(&g_playlist, status);
return COMMAND_RETURN_OK;
}
@@ -1157,7 +1264,7 @@ handle_random(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
return COMMAND_RETURN_ERROR;
}
setPlaylistRandomStatus(&g_playlist, status);
playlist_set_random(&g_playlist, status);
return COMMAND_RETURN_OK;
}
@@ -1172,7 +1279,7 @@ static enum command_return
handle_clearerror(G_GNUC_UNUSED struct client *client,
G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
{
clearPlayerError();
pc_clear_error();
return COMMAND_RETURN_OK;
}
@@ -1196,17 +1303,17 @@ handle_list(struct client *client, int argc, char *argv[])
/* for compatibility with < 0.12.0 */
if (argc == 3) {
if (tagType != TAG_ITEM_ALBUM) {
if (tagType != TAG_ALBUM) {
command_error(client, ACK_ERROR_ARG,
"should be \"%s\" for 3 arguments",
tag_item_names[TAG_ITEM_ALBUM]);
tag_item_names[TAG_ALBUM]);
return COMMAND_RETURN_ERROR;
}
locate_item_list_parse(argv + 1, argc - 1);
conditionals = locate_item_list_new(1);
conditionals->items[0].tag = TAG_ITEM_ARTIST;
conditionals->items[0].tag = TAG_ARTIST;
conditionals->items[0].needle = g_strdup(argv[2]);
} else {
conditionals =
@@ -1241,7 +1348,7 @@ handle_move(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
return COMMAND_RETURN_ERROR;
if (!check_int(client, &to, argv[2], check_integer, argv[2]))
return COMMAND_RETURN_ERROR;
result = moveSongRangeInPlaylist(&g_playlist, start, end, to);
result = playlist_move_range(&g_playlist, start, end, to);
return print_playlist_result(client, result);
}
@@ -1255,7 +1362,7 @@ handle_moveid(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
return COMMAND_RETURN_ERROR;
if (!check_int(client, &to, argv[2], check_integer, argv[2]))
return COMMAND_RETURN_ERROR;
result = moveSongInPlaylistById(&g_playlist, id, to);
result = playlist_move_id(&g_playlist, id, to);
return print_playlist_result(client, result);
}
@@ -1269,7 +1376,7 @@ handle_swap(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
return COMMAND_RETURN_ERROR;
if (!check_int(client, &song2, argv[2], check_integer, argv[2]))
return COMMAND_RETURN_ERROR;
result = swapSongsInPlaylist(&g_playlist, song1, song2);
result = playlist_swap_songs(&g_playlist, song1, song2);
return print_playlist_result(client, result);
}
@@ -1283,7 +1390,7 @@ handle_swapid(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
return COMMAND_RETURN_ERROR;
if (!check_int(client, &id2, argv[2], check_integer, argv[2]))
return COMMAND_RETURN_ERROR;
result = swapSongsInPlaylistById(&g_playlist, id1, id2);
result = playlist_swap_songs_id(&g_playlist, id1, id2);
return print_playlist_result(client, result);
}
@@ -1298,7 +1405,7 @@ handle_seek(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
if (!check_int(client, &seek_time, argv[2], check_integer, argv[2]))
return COMMAND_RETURN_ERROR;
result = seekSongInPlaylist(&g_playlist, song, seek_time);
result = playlist_seek_song(&g_playlist, song, seek_time);
return print_playlist_result(client, result);
}
@@ -1313,7 +1420,7 @@ handle_seekid(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
if (!check_int(client, &seek_time, argv[2], check_integer, argv[2]))
return COMMAND_RETURN_ERROR;
result = seekSongInPlaylistById(&g_playlist, id, seek_time);
result = playlist_seek_song_id(&g_playlist, id, seek_time);
return print_playlist_result(client, result);
}
@@ -1363,7 +1470,31 @@ handle_crossfade(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
if (!check_unsigned(client, &xfade_time, argv[1]))
return COMMAND_RETURN_ERROR;
setPlayerCrossFade(xfade_time);
pc_set_cross_fade(xfade_time);
return COMMAND_RETURN_OK;
}
static enum command_return
handle_mixrampdb(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
{
float db;
if (!check_float(client, &db, argv[1]))
return COMMAND_RETURN_ERROR;
pc_set_mixramp_db(db);
return COMMAND_RETURN_OK;
}
static enum command_return
handle_mixrampdelay(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
{
float delay_secs;
if (!check_float(client, &delay_secs, argv[1]))
return COMMAND_RETURN_ERROR;
pc_set_mixramp_delay(delay_secs);
return COMMAND_RETURN_OK;
}
@@ -1476,6 +1607,28 @@ handle_listplaylists(struct client *client,
return COMMAND_RETURN_OK;
}
static enum command_return
handle_replay_gain_mode(struct client *client,
G_GNUC_UNUSED int argc, char *argv[])
{
if (!replay_gain_set_mode_string(argv[1])) {
command_error(client, ACK_ERROR_ARG,
"Unrecognized replay gain mode");
return COMMAND_RETURN_ERROR;
}
return COMMAND_RETURN_OK;
}
static enum command_return
handle_replay_gain_status(struct client *client,
G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
{
client_printf(client, "replay_gain_mode: %s\n",
replay_gain_get_mode_string());
return COMMAND_RETURN_OK;
}
static enum command_return
handle_idle(struct client *client,
G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
@@ -1519,13 +1672,14 @@ sticker_song_find_print_cb(struct song *song, const char *value,
{
struct sticker_song_find_data *data = user_data;
song_print_url(data->client, song);
song_print_uri(data->client, song);
sticker_print_value(data->client, data->name, value);
}
static enum command_return
handle_sticker_song(struct client *client, int argc, char *argv[])
{
/* get song song_id key */
if (argc == 5 && strcmp(argv[1], "get") == 0) {
struct song *song;
char *value;
@@ -1548,6 +1702,7 @@ handle_sticker_song(struct client *client, int argc, char *argv[])
g_free(value);
return COMMAND_RETURN_OK;
/* list song song_id */
} else if (argc == 4 && strcmp(argv[1], "list") == 0) {
struct song *song;
struct sticker *sticker;
@@ -1570,6 +1725,7 @@ handle_sticker_song(struct client *client, int argc, char *argv[])
sticker_free(sticker);
return COMMAND_RETURN_OK;
/* set song song_id id key */
} else if (argc == 6 && strcmp(argv[1], "set") == 0) {
struct song *song;
bool ret;
@@ -1589,6 +1745,7 @@ handle_sticker_song(struct client *client, int argc, char *argv[])
}
return COMMAND_RETURN_OK;
/* delete song song_id [key] */
} else if ((argc == 4 || argc == 5) &&
strcmp(argv[1], "delete") == 0) {
struct song *song;
@@ -1611,6 +1768,7 @@ handle_sticker_song(struct client *client, int argc, char *argv[])
}
return COMMAND_RETURN_OK;
/* find song dir key */
} else if (argc == 5 && strcmp(argv[1], "find") == 0) {
/* "sticker find song a/directory name" */
struct directory *directory;
@@ -1679,11 +1837,13 @@ static const struct command commands[] = {
{ "count", PERMISSION_READ, 2, -1, handle_count },
{ "crossfade", PERMISSION_CONTROL, 1, 1, handle_crossfade },
{ "currentsong", PERMISSION_READ, 0, 0, handle_currentsong },
{ "decoders", PERMISSION_READ, 0, 0, handle_decoders },
{ "delete", PERMISSION_CONTROL, 1, 1, handle_delete },
{ "deleteid", PERMISSION_CONTROL, 1, 1, handle_deleteid },
{ "disableoutput", PERMISSION_ADMIN, 1, 1, handle_disableoutput },
{ "enableoutput", PERMISSION_ADMIN, 1, 1, handle_enableoutput },
{ "find", PERMISSION_READ, 2, -1, handle_find },
{ "findadd", PERMISSION_READ, 2, -1, handle_findadd},
{ "idle", PERMISSION_READ, 0, -1, handle_idle },
{ "kill", PERMISSION_ADMIN, -1, -1, handle_kill },
{ "list", PERMISSION_READ, 1, -1, handle_list },
@@ -1694,6 +1854,8 @@ static const struct command commands[] = {
{ "listplaylists", PERMISSION_READ, 0, 0, handle_listplaylists },
{ "load", PERMISSION_ADD, 1, 1, handle_load },
{ "lsinfo", PERMISSION_READ, 0, 1, handle_lsinfo },
{ "mixrampdb", PERMISSION_CONTROL, 1, 1, handle_mixrampdb },
{ "mixrampdelay", PERMISSION_CONTROL, 1, 1, handle_mixrampdelay },
{ "move", PERMISSION_CONTROL, 2, 2, handle_move },
{ "moveid", PERMISSION_CONTROL, 2, 2, handle_moveid },
{ "next", PERMISSION_CONTROL, 0, 0, handle_next },
@@ -1719,6 +1881,11 @@ static const struct command commands[] = {
{ "random", PERMISSION_CONTROL, 1, 1, handle_random },
{ "rename", PERMISSION_CONTROL, 2, 2, handle_rename },
{ "repeat", PERMISSION_CONTROL, 1, 1, handle_repeat },
{ "replay_gain_mode", PERMISSION_CONTROL, 1, 1,
handle_replay_gain_mode },
{ "replay_gain_status", PERMISSION_READ, 0, 0,
handle_replay_gain_status },
{ "rescan", PERMISSION_ADMIN, 0, 1, handle_rescan },
{ "rm", PERMISSION_CONTROL, 1, 1, handle_rm },
{ "save", PERMISSION_CONTROL, 1, 1, handle_save },
{ "search", PERMISSION_READ, 2, -1, handle_search },
@@ -1738,7 +1905,6 @@ static const struct command commands[] = {
{ "tagtypes", PERMISSION_READ, 0, 0, handle_tagtypes },
{ "update", PERMISSION_ADMIN, 0, 1, handle_update },
{ "urlhandlers", PERMISSION_READ, 0, 0, handle_urlhandlers },
{ "volume", PERMISSION_CONTROL, 1, 1, handle_volume },
};
static const unsigned num_commands = sizeof(commands) / sizeof(commands[0]);
@@ -1892,15 +2058,63 @@ command_checked_lookup(struct client *client, unsigned permission,
}
enum command_return
command_process(struct client *client, char *commandString)
command_process(struct client *client, unsigned num, char *line)
{
GError *error = NULL;
int argc;
char *argv[COMMAND_ARGV_MAX] = { NULL };
const struct command *cmd;
enum command_return ret = COMMAND_RETURN_ERROR;
if (!(argc = buffer2array(commandString, argv, COMMAND_ARGV_MAX)))
return COMMAND_RETURN_OK;
command_list_num = num;
/* get the command name (first word on the line) */
argv[0] = tokenizer_next_word(&line, &error);
if (argv[0] == NULL) {
current_command = "";
if (*line == 0)
command_error(client, ACK_ERROR_UNKNOWN,
"No command given");
else {
command_error(client, ACK_ERROR_UNKNOWN,
"%s", error->message);
g_error_free(error);
}
current_command = NULL;
return COMMAND_RETURN_ERROR;
}
argc = 1;
/* now parse the arguments (quoted or unquoted) */
while (argc < (int)G_N_ELEMENTS(argv) &&
(argv[argc] =
tokenizer_next_param(&line, &error)) != NULL)
++argc;
/* some error checks; we have to set current_command because
command_error() expects it to be set */
current_command = argv[0];
if (argc >= (int)G_N_ELEMENTS(argv)) {
command_error(client, ACK_ERROR_ARG, "Too many arguments");
current_command = NULL;
return COMMAND_RETURN_ERROR;
}
if (*line != 0) {
command_error(client, ACK_ERROR_ARG,
"%s", error->message);
current_command = NULL;
g_error_free(error);
return COMMAND_RETURN_ERROR;
}
/* look up and invoke the command handler */
cmd = command_checked_lookup(client, client_get_permission(client),
argc, argv);
@@ -1908,32 +2122,7 @@ command_process(struct client *client, char *commandString)
ret = cmd->handler(client, argc, argv);
current_command = NULL;
command_list_num = 0;
return ret;
}
enum command_return
command_process_list(struct client *client,
bool list_ok, GSList *list)
{
enum command_return ret = COMMAND_RETURN_OK;
command_list_num = 0;
for (GSList *cur = list; cur != NULL; cur = g_slist_next(cur)) {
char *cmd = cur->data;
g_debug("command_process_list: process command \"%s\"",
cmd);
ret = command_process(client, cmd);
g_debug("command_process_list: command returned %i", ret);
if (ret != COMMAND_RETURN_OK || client_is_expired(client))
break;
else if (list_ok)
client_puts(client, "list_OK\n");
command_list_num++;
}
command_list_num = 0;
return ret;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2003-2009 The Music Player Daemon Project
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -39,11 +39,7 @@ void command_init(void);
void command_finish(void);
enum command_return
command_process_list(struct client *client,
bool list_ok, GSList *list);
enum command_return
command_process(struct client *client, char *commandString);
command_process(struct client *client, unsigned num, char *line);
void command_success(struct client *client);

View File

@@ -1,410 +0,0 @@
/*
* Copyright (C) 2003-2009 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program 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.
*
* This program 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 this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/*
* Imported from AudioCompress by J. Shagam <fluffy@beesbuzz.biz>
*/
#include "compress.h"
#include <glib.h>
#include <stdint.h>
#include <string.h>
#ifdef USE_X
#include <X11/Xlib.h>
#include <X11/Xutil.h>
static Display *display;
static Window window;
static Visual *visual;
static int screen;
static GC blackGC, whiteGC, blueGC, yellowGC, dkyellowGC, redGC;
#endif
static int *peaks;
static int gainCurrent, gainTarget;
static struct {
int show_mon;
int anticlip;
int target;
int gainmax;
int gainsmooth;
unsigned buckets;
} prefs;
#ifdef USE_X
static int mon_init;
#endif
void CompressCfg(int show_mon, int anticlip, int target, int gainmax,
int gainsmooth, unsigned buckets)
{
static unsigned lastsize;
prefs.show_mon = show_mon;
prefs.anticlip = anticlip;
prefs.target = target;
prefs.gainmax = gainmax;
prefs.gainsmooth = gainsmooth;
prefs.buckets = buckets;
/* Allocate the peak structure */
peaks = g_realloc(peaks, sizeof(int)*prefs.buckets);
if (prefs.buckets > lastsize)
memset(peaks + lastsize, 0, sizeof(int)*(prefs.buckets
- lastsize));
lastsize = prefs.buckets;
#ifdef USE_X
/* Configure the monitor window if needed */
if (show_mon && !mon_init)
{
display = XOpenDisplay(getenv("DISPLAY"));
/* We really shouldn't try to init X if there's no X */
if (!display)
{
fprintf(stderr,
"X not detected; disabling monitor window\n");
show_mon = prefs.show_mon = 0;
}
}
if (show_mon && !mon_init)
{
XGCValues gcv;
XColor col;
gainCurrent = gainTarget = (1 << GAINSHIFT);
screen = DefaultScreen(display);
visual = DefaultVisual(display, screen);
window = XCreateSimpleWindow(display,
RootWindow(display, screen),
0, 0, prefs.buckets, 128 + 8, 0,
BlackPixel(display, screen),
WhitePixel(display, screen));
XStoreName(display, window, "AudioCompress monitor");
gcv.foreground = BlackPixel(display, screen);
blackGC = XCreateGC(display, window, GCForeground, &gcv);
gcv.foreground = WhitePixel(display, screen);
whiteGC = XCreateGC(display, window, GCForeground, &gcv);
col.red = 0;
col.green = 0;
col.blue = 65535;
XAllocColor(display, DefaultColormap(display, screen), &col);
gcv.foreground = col.pixel;
blueGC = XCreateGC(display, window, GCForeground, &gcv);
col.red = 65535;
col.green = 65535;
col.blue = 0;
XAllocColor(display, DefaultColormap(display, screen), &col);
gcv.foreground = col.pixel;
yellowGC = XCreateGC(display, window, GCForeground, &gcv);
col.red = 32767;
col.green = 32767;
col.blue = 0;
XAllocColor(display, DefaultColormap(display, screen), &col);
gcv.foreground = col.pixel;
dkyellowGC = XCreateGC(display, window, GCForeground, &gcv);
col.red = 65535;
col.green = 0;
col.blue = 0;
XAllocColor(display, DefaultColormap(display, screen), &col);
gcv.foreground = col.pixel;
redGC = XCreateGC(display, window, GCForeground, &gcv);
mon_init = 1;
}
if (mon_init)
{
if (show_mon)
XMapWindow(display, window);
else
XUnmapWindow(display, window);
XResizeWindow(display, window, prefs.buckets, 128 + 8);
XFlush(display);
}
#endif
}
void CompressFree(void)
{
#ifdef USE_X
if (mon_init)
{
XFreeGC(display, blackGC);
XFreeGC(display, whiteGC);
XFreeGC(display, blueGC);
XFreeGC(display, yellowGC);
XFreeGC(display, dkyellowGC);
XFreeGC(display, redGC);
XDestroyWindow(display, window);
XCloseDisplay(display);
}
#endif
g_free(peaks);
}
void CompressDo(void *data, unsigned int length)
{
int16_t *audio = (int16_t *)data, *ap;
int peak;
unsigned int i, pos;
int gr, gf, gn;
static int pn = -1;
#ifdef STATS
static int clip;
#endif
static int clipped;
if (!peaks)
return;
if (pn == -1)
{
for (i = 0; i < prefs.buckets; i++)
peaks[i] = 0;
}
pn = (pn + 1)%prefs.buckets;
#ifdef DEBUG
fprintf(stderr, "modifyNative16(0x%08x, %d)\n",(unsigned int)data,
length);
#endif
/* Determine peak's value and position */
peak = 1;
pos = 0;
#ifdef DEBUG
fprintf(stderr, "finding peak(b=%d)\n", pn);
#endif
ap = audio;
for (i = 0; i < length/2; i++)
{
int val = *ap;
if (val > peak)
{
peak = val;
pos = i;
} else if (-val > peak)
{
peak = -val;
pos = i;
}
ap++;
}
peaks[pn] = peak;
/* Only draw if needed, of course */
#ifdef USE_X
if (prefs.show_mon)
{
/* current amplitude */
XDrawLine(display, window, whiteGC,
pn, 0,
pn,
127 -
(peaks[pn]*gainCurrent >> (GAINSHIFT + 8)));
/* amplification */
XDrawLine(display, window, yellowGC,
pn,
127 - (peaks[pn]*gainCurrent
>> (GAINSHIFT + 8)),
pn, 127);
/* peak */
XDrawLine(display, window, blackGC,
pn, 127 - (peaks[pn] >> 8), pn, 127);
/* clip indicator */
if (clipped)
XDrawLine(display, window, redGC,
(pn + prefs.buckets - 1)%prefs.buckets,
126 - clipped/(length*512),
(pn + prefs.buckets - 1)%prefs.buckets,
127);
clipped = 0;
/* target line */
/* XDrawPoint(display, window, redGC, */
/* pn, 127 - TARGET/256); */
/* amplification edge */
XDrawLine(display, window, dkyellowGC,
pn,
127 - (peaks[pn]*gainCurrent
>> (GAINSHIFT + 8)),
pn - 1,
127 -
(peaks[(pn + prefs.buckets
- 1)%prefs.buckets]*gainCurrent
>> (GAINSHIFT + 8)));
}
#endif
for (i = 0; i < prefs.buckets; i++)
{
if (peaks[i] > peak)
{
peak = peaks[i];
pos = 0;
}
}
/* Determine target gain */
gn = (1 << GAINSHIFT)*prefs.target/peak;
if (gn <(1 << GAINSHIFT))
gn = 1 << GAINSHIFT;
gainTarget = (gainTarget *((1 << prefs.gainsmooth) - 1) + gn)
>> prefs.gainsmooth;
/* Give it an extra insignifigant nudge to counteract possible
** rounding error
*/
if (gn < gainTarget)
gainTarget--;
else if (gn > gainTarget)
gainTarget++;
if (gainTarget > prefs.gainmax << GAINSHIFT)
gainTarget = prefs.gainmax << GAINSHIFT;
#ifdef USE_X
if (prefs.show_mon)
{
int x;
/* peak*gain */
XDrawPoint(display, window, redGC,
pn,
127 - (peak*gainCurrent
>> (GAINSHIFT + 8)));
/* gain indicator */
XFillRectangle(display, window, whiteGC, 0, 128,
prefs.buckets, 8);
x = (gainTarget - (1 << GAINSHIFT))*prefs.buckets
/ ((prefs.gainmax - 1) << GAINSHIFT);
XDrawLine(display, window, redGC, x,
128, x, 128 + 8);
x = (gn - (1 << GAINSHIFT))*prefs.buckets
/ ((prefs.gainmax - 1) << GAINSHIFT);
XDrawLine(display, window, blackGC,
x, 132 - 1,
x, 132 + 1);
/* blue peak line */
XDrawLine(display, window, blueGC,
0, 127 - (peak >> 8), prefs.buckets,
127 - (peak >> 8));
XFlush(display);
XDrawLine(display, window, whiteGC,
0, 127 - (peak >> 8), prefs.buckets,
127 - (peak >> 8));
}
#endif
/* See if a peak is going to clip */
gn = (1 << GAINSHIFT)*32768/peak;
if (gn < gainTarget)
{
gainTarget = gn;
if (prefs.anticlip)
pos = 0;
} else
{
/* We're ramping up, so draw it out over the whole frame */
pos = length;
}
/* Determine gain rate necessary to make target */
if (!pos)
pos = 1;
gr = ((gainTarget - gainCurrent) << 16)/(int)pos;
/* Do the shiznit */
gf = gainCurrent << 16;
#ifdef STATS
fprintf(stderr, "\rgain = %2.2f%+.2e ",
gainCurrent*1.0/(1 << GAINSHIFT),
(gainTarget - gainCurrent)*1.0/(1 << GAINSHIFT));
#endif
ap = audio;
for (i = 0; i < length/2; i++)
{
int sample;
/* Interpolate the gain */
gainCurrent = gf >> 16;
if (i < pos)
gf += gr;
else if (i == pos)
gf = gainTarget << 16;
/* Amplify */
sample = (*ap)*gainCurrent >> GAINSHIFT;
if (sample < -32768)
{
#ifdef STATS
clip++;
#endif
clipped += -32768 - sample;
sample = -32768;
} else if (sample > 32767)
{
#ifdef STATS
clip++;
#endif
clipped += sample - 32767;
sample = 32767;
}
*ap++ = sample;
}
#ifdef STATS
fprintf(stderr, "clip %d b%-3d ", clip, pn);
#endif
#ifdef DEBUG
fprintf(stderr, "\ndone\n");
#endif
}

View File

@@ -1,48 +0,0 @@
/*
* Copyright (C) 2003-2009 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program 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.
*
* This program 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 this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/*
* Imported from AudioCompress by J. Shagam <fluffy@beesbuzz.biz>
*/
#ifndef MPD_COMPRESS_H
#define MPD_COMPRESS_H
/* These are copied from the AudioCompress config.h, mainly because CompressDo
* needs GAINSHIFT defined. The rest are here so they can be used as defaults
* to pass to CompressCfg(). -- jat */
#define ANTICLIP 0 /* Strict clipping protection */
#define TARGET 25000 /* Target level */
#define GAINMAX 32 /* The maximum amount to amplify by */
#define GAINSHIFT 10 /* How fine-grained the gain is */
#define GAINSMOOTH 8 /* How much inertia ramping has*/
#define BUCKETS 400 /* How long of a history to store */
void CompressCfg(int monitor,
int anticlip,
int target,
int maxgain,
int smooth,
unsigned buckets);
void CompressDo(void *data, unsigned int numSamples);
void CompressFree(void);
#endif

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2003-2009 The Music Player Daemon Project
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,10 +17,13 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h"
#include "conf.h"
#include "utils.h"
#include "buffer2array.h"
#include "tokenizer.h"
#include "path.h"
#include "glib_compat.h"
#include "mpd_error.h"
#include <glib.h>
@@ -36,37 +39,84 @@
#define MAX_STRING_SIZE MPD_PATH_MAX+80
#define CONF_COMMENT '#'
#define CONF_BLOCK_BEGIN "{"
#define CONF_BLOCK_END "}"
#define CONF_REPEATABLE_MASK 0x01
#define CONF_BLOCK_MASK 0x02
#define CONF_LINE_TOKEN_MAX 3
struct config_entry {
const char *name;
unsigned char mask;
const char *const name;
const bool repeatable;
const bool block;
GSList *params;
};
static GSList *config_entries;
static struct config_entry config_entries[] = {
{ .name = CONF_MUSIC_DIR, false, false },
{ .name = CONF_PLAYLIST_DIR, false, false },
{ .name = CONF_FOLLOW_INSIDE_SYMLINKS, false, false },
{ .name = CONF_FOLLOW_OUTSIDE_SYMLINKS, false, false },
{ .name = CONF_DB_FILE, false, false },
{ .name = CONF_STICKER_FILE, false, false },
{ .name = CONF_LOG_FILE, false, false },
{ .name = CONF_PID_FILE, false, false },
{ .name = CONF_STATE_FILE, false, false },
{ .name = CONF_USER, false, false },
{ .name = CONF_GROUP, false, false },
{ .name = CONF_BIND_TO_ADDRESS, true, false },
{ .name = CONF_PORT, false, false },
{ .name = CONF_LOG_LEVEL, false, false },
{ .name = CONF_ZEROCONF_NAME, false, false },
{ .name = CONF_ZEROCONF_ENABLED, false, false },
{ .name = CONF_PASSWORD, true, false },
{ .name = CONF_DEFAULT_PERMS, false, false },
{ .name = CONF_AUDIO_OUTPUT, true, true },
{ .name = CONF_AUDIO_OUTPUT_FORMAT, false, false },
{ .name = CONF_MIXER_TYPE, false, false },
{ .name = CONF_REPLAYGAIN, false, false },
{ .name = CONF_REPLAYGAIN_PREAMP, false, false },
{ .name = CONF_REPLAYGAIN_MISSING_PREAMP, false, false },
{ .name = CONF_REPLAYGAIN_LIMIT, false, false },
{ .name = CONF_VOLUME_NORMALIZATION, false, false },
{ .name = CONF_SAMPLERATE_CONVERTER, false, false },
{ .name = CONF_AUDIO_BUFFER_SIZE, false, false },
{ .name = CONF_BUFFER_BEFORE_PLAY, false, false },
{ .name = CONF_HTTP_PROXY_HOST, false, false },
{ .name = CONF_HTTP_PROXY_PORT, false, false },
{ .name = CONF_HTTP_PROXY_USER, false, false },
{ .name = CONF_HTTP_PROXY_PASSWORD, false, false },
{ .name = CONF_CONN_TIMEOUT, false, false },
{ .name = CONF_MAX_CONN, false, false },
{ .name = CONF_MAX_PLAYLIST_LENGTH, false, false },
{ .name = CONF_MAX_COMMAND_LIST_SIZE, false, false },
{ .name = CONF_MAX_OUTPUT_BUFFER_SIZE, false, false },
{ .name = CONF_FS_CHARSET, false, false },
{ .name = CONF_ID3V1_ENCODING, false, false },
{ .name = CONF_METADATA_TO_USE, false, false },
{ .name = CONF_SAVE_ABSOLUTE_PATHS, false, false },
{ .name = CONF_DECODER, true, true },
{ .name = CONF_INPUT, true, true },
{ .name = CONF_GAPLESS_MP3_PLAYBACK, false, false },
{ .name = CONF_PLAYLIST_PLUGIN, true, true },
{ .name = CONF_AUTO_UPDATE, false, false },
{ .name = CONF_AUTO_UPDATE_DEPTH, false, false },
{ .name = "filter", true, true },
};
static int get_bool(const char *value)
static bool
get_bool(const char *value, bool *value_r)
{
const char **x;
static const char *t[] = { "yes", "true", "1", NULL };
static const char *f[] = { "no", "false", "0", NULL };
for (x = t; *x; x++) {
if (!g_ascii_strcasecmp(*x, value))
return 1;
if (string_array_contains(t, value)) {
*value_r = true;
return true;
}
for (x = f; *x; x++) {
if (!g_ascii_strcasecmp(*x, value))
return 0;
if (string_array_contains(f, value)) {
*value_r = false;
return true;
}
return CONF_BOOL_INVALID;
return false;
}
struct config_param *
@@ -83,15 +133,14 @@ config_new_param(const char *value, int line)
ret->num_block_params = 0;
ret->block_params = NULL;
ret->used = false;
return ret;
}
static void
config_param_free(gpointer data, G_GNUC_UNUSED gpointer user_data)
config_param_free(struct config_param *param)
{
struct config_param *param = data;
g_free(param->value);
for (unsigned i = 0; i < param->num_block_params; i++) {
@@ -105,42 +154,19 @@ config_param_free(gpointer data, G_GNUC_UNUSED gpointer user_data)
g_free(param);
}
static struct config_entry *
newConfigEntry(const char *name, int repeatable, int block)
{
struct config_entry *ret = g_new(struct config_entry, 1);
ret->name = name;
ret->mask = 0;
ret->params = NULL;
if (repeatable)
ret->mask |= CONF_REPEATABLE_MASK;
if (block)
ret->mask |= CONF_BLOCK_MASK;
return ret;
}
static void
config_entry_free(gpointer data, G_GNUC_UNUSED gpointer user_data)
config_param_free_callback(gpointer data, G_GNUC_UNUSED gpointer user_data)
{
struct config_entry *entry = data;
struct config_param *param = data;
g_slist_foreach(entry->params, config_param_free, NULL);
g_slist_free(entry->params);
g_free(entry);
config_param_free(param);
}
static struct config_entry *
config_entry_get(const char *name)
{
GSList *list;
for (list = config_entries; list != NULL;
list = g_slist_next(list)) {
struct config_entry *entry = list->data;
for (unsigned i = 0; i < G_N_ELEMENTS(config_entries); ++i) {
struct config_entry *entry = &config_entries[i];
if (strcmp(entry->name, name) == 0)
return entry;
}
@@ -148,82 +174,65 @@ config_entry_get(const char *name)
return NULL;
}
static void registerConfigParam(const char *name, int repeatable, int block)
{
struct config_entry *entry;
entry = config_entry_get(name);
if (entry != NULL)
g_error("config parameter \"%s\" already registered\n", name);
entry = newConfigEntry(name, repeatable, block);
config_entries = g_slist_prepend(config_entries, entry);
}
void config_global_finish(void)
{
g_slist_foreach(config_entries, config_entry_free, NULL);
g_slist_free(config_entries);
for (unsigned i = 0; i < G_N_ELEMENTS(config_entries); ++i) {
struct config_entry *entry = &config_entries[i];
g_slist_foreach(entry->params,
config_param_free_callback, NULL);
g_slist_free(entry->params);
}
}
void config_global_init(void)
{
config_entries = NULL;
/* registerConfigParam(name, repeatable, block); */
registerConfigParam(CONF_MUSIC_DIR, 0, 0);
registerConfigParam(CONF_PLAYLIST_DIR, 0, 0);
registerConfigParam(CONF_FOLLOW_INSIDE_SYMLINKS, 0, 0);
registerConfigParam(CONF_FOLLOW_OUTSIDE_SYMLINKS, 0, 0);
registerConfigParam(CONF_DB_FILE, 0, 0);
registerConfigParam(CONF_STICKER_FILE, false, false);
registerConfigParam(CONF_LOG_FILE, 0, 0);
registerConfigParam(CONF_ERROR_FILE, 0, 0);
registerConfigParam(CONF_PID_FILE, 0, 0);
registerConfigParam(CONF_STATE_FILE, 0, 0);
registerConfigParam(CONF_USER, 0, 0);
registerConfigParam(CONF_BIND_TO_ADDRESS, 1, 0);
registerConfigParam(CONF_PORT, 0, 0);
registerConfigParam(CONF_LOG_LEVEL, 0, 0);
registerConfigParam(CONF_ZEROCONF_NAME, 0, 0);
registerConfigParam(CONF_ZEROCONF_ENABLED, 0, 0);
registerConfigParam(CONF_PASSWORD, 1, 0);
registerConfigParam(CONF_DEFAULT_PERMS, 0, 0);
registerConfigParam(CONF_AUDIO_OUTPUT, 1, 1);
registerConfigParam(CONF_AUDIO_OUTPUT_FORMAT, 0, 0);
registerConfigParam(CONF_MIXER_TYPE, 0, 0);
registerConfigParam(CONF_MIXER_DEVICE, 0, 0);
registerConfigParam(CONF_MIXER_CONTROL, 0, 0);
registerConfigParam(CONF_REPLAYGAIN, 0, 0);
registerConfigParam(CONF_REPLAYGAIN_PREAMP, 0, 0);
registerConfigParam(CONF_VOLUME_NORMALIZATION, 0, 0);
registerConfigParam(CONF_SAMPLERATE_CONVERTER, 0, 0);
registerConfigParam(CONF_AUDIO_BUFFER_SIZE, 0, 0);
registerConfigParam(CONF_BUFFER_BEFORE_PLAY, 0, 0);
registerConfigParam(CONF_HTTP_PROXY_HOST, 0, 0);
registerConfigParam(CONF_HTTP_PROXY_PORT, 0, 0);
registerConfigParam(CONF_HTTP_PROXY_USER, 0, 0);
registerConfigParam(CONF_HTTP_PROXY_PASSWORD, 0, 0);
registerConfigParam(CONF_CONN_TIMEOUT, 0, 0);
registerConfigParam(CONF_MAX_CONN, 0, 0);
registerConfigParam(CONF_MAX_PLAYLIST_LENGTH, 0, 0);
registerConfigParam(CONF_MAX_COMMAND_LIST_SIZE, 0, 0);
registerConfigParam(CONF_MAX_OUTPUT_BUFFER_SIZE, 0, 0);
registerConfigParam(CONF_FS_CHARSET, 0, 0);
registerConfigParam(CONF_ID3V1_ENCODING, 0, 0);
registerConfigParam(CONF_METADATA_TO_USE, 0, 0);
registerConfigParam(CONF_SAVE_ABSOLUTE_PATHS, 0, 0);
registerConfigParam(CONF_DECODER, true, true);
registerConfigParam(CONF_INPUT, true, true);
registerConfigParam(CONF_GAPLESS_MP3_PLAYBACK, 0, 0);
}
void
static void
config_param_check(gpointer data, G_GNUC_UNUSED gpointer user_data)
{
struct config_param *param = data;
if (!param->used)
/* this whole config_param was not queried at all -
the feature might be disabled at compile time?
Silently ignore it here. */
return;
for (unsigned i = 0; i < param->num_block_params; i++) {
struct block_param *bp = &param->block_params[i];
if (!bp->used)
g_warning("option '%s' on line %i was not recognized",
bp->name, bp->line);
}
}
void config_global_check(void)
{
for (unsigned i = 0; i < G_N_ELEMENTS(config_entries); ++i) {
struct config_entry *entry = &config_entries[i];
g_slist_foreach(entry->params, config_param_check, NULL);
}
}
bool
config_add_block_param(struct config_param * param, const char *name,
const char *value, int line)
const char *value, int line, GError **error_r)
{
struct block_param *bp;
bp = config_get_block_param(param, name);
if (bp != NULL) {
g_set_error(error_r, config_quark(), 0,
"\"%s\" first defined on line %i, and "
"redefined on line %i\n", name,
bp->line, line);
return false;
}
param->num_block_params++;
param->block_params = g_realloc(param->block_params,
@@ -235,67 +244,97 @@ config_add_block_param(struct config_param * param, const char *name,
bp->name = g_strdup(name);
bp->value = g_strdup(value);
bp->line = line;
bp->used = false;
return true;
}
static struct config_param *
config_read_block(FILE *fp, int *count, char *string)
config_read_block(FILE *fp, int *count, char *string, GError **error_r)
{
struct config_param *ret = config_new_param(NULL, *count);
GError *error = NULL;
bool success;
int i;
int numberOfArgs;
int argsMinusComment;
while (true) {
char *line;
const char *name, *value;
while (fgets(string, MAX_STRING_SIZE, fp)) {
char *array[CONF_LINE_TOKEN_MAX] = { NULL };
line = fgets(string, MAX_STRING_SIZE, fp);
if (line == NULL) {
config_param_free(ret);
g_set_error(error_r, config_quark(), 0,
"Expected '}' before end-of-file");
return NULL;
}
(*count)++;
numberOfArgs = buffer2array(string, array, CONF_LINE_TOKEN_MAX);
for (i = 0; i < numberOfArgs; i++) {
if (array[i][0] == CONF_COMMENT)
break;
}
argsMinusComment = i;
if (0 == argsMinusComment) {
line = g_strchug(line);
if (*line == 0 || *line == CONF_COMMENT)
continue;
}
if (1 == argsMinusComment &&
0 == strcmp(array[0], CONF_BLOCK_END)) {
break;
}
if (*line == '}') {
/* end of this block; return from the function
(and from this "while" loop) */
if (2 != argsMinusComment) {
g_error("improperly formatted config file at line %i:"
" %s\n", *count, string);
}
if (0 == strcmp(array[0], CONF_BLOCK_BEGIN) ||
0 == strcmp(array[1], CONF_BLOCK_BEGIN) ||
0 == strcmp(array[0], CONF_BLOCK_END) ||
0 == strcmp(array[1], CONF_BLOCK_END)) {
g_error("improperly formatted config file at line %i: %s "
"in block beginning at line %i\n",
*count, string, ret->line);
}
config_add_block_param(ret, array[0], array[1], *count);
line = g_strchug(line + 1);
if (*line != 0 && *line != CONF_COMMENT) {
config_param_free(ret);
g_set_error(error_r, config_quark(), 0,
"line %i: Unknown tokens after '}'",
*count);
return false;
}
return ret;
}
/* parse name and value */
name = tokenizer_next_word(&line, &error);
if (name == NULL) {
assert(*line != 0);
config_param_free(ret);
g_propagate_prefixed_error(error_r, error,
"line %i: ", *count);
return NULL;
}
value = tokenizer_next_string(&line, &error);
if (value == NULL) {
config_param_free(ret);
if (*line == 0)
g_set_error(error_r, config_quark(), 0,
"line %i: Value missing", *count);
else
g_propagate_prefixed_error(error_r, error,
"line %i: ",
*count);
return NULL;
}
if (*line != 0 && *line != CONF_COMMENT) {
config_param_free(ret);
g_set_error(error_r, config_quark(), 0,
"line %i: Unknown tokens after value",
*count);
return NULL;
}
success = config_add_block_param(ret, name, value, *count,
error_r);
if (!success) {
config_param_free(ret);
return false;
}
}
}
void config_read_file(const char *file)
bool
config_read_file(const char *file, GError **error_r)
{
FILE *fp;
char string[MAX_STRING_SIZE + 1];
int i;
int numberOfArgs;
int argsMinusComment;
int count = 0;
struct config_entry *entry;
struct config_param *param;
@@ -303,67 +342,110 @@ void config_read_file(const char *file)
g_debug("loading file %s", file);
if (!(fp = fopen(file, "r"))) {
g_error("problems opening file %s for reading: %s\n",
g_set_error(error_r, config_quark(), errno,
"Failed to open %s: %s",
file, strerror(errno));
return false;
}
while (fgets(string, MAX_STRING_SIZE, fp)) {
char *array[CONF_LINE_TOKEN_MAX] = { NULL };
char *line;
const char *name, *value;
GError *error = NULL;
count++;
numberOfArgs = buffer2array(string, array, CONF_LINE_TOKEN_MAX);
for (i = 0; i < numberOfArgs; i++) {
if (array[i][0] == CONF_COMMENT)
break;
}
argsMinusComment = i;
if (0 == argsMinusComment) {
line = g_strchug(string);
if (*line == 0 || *line == CONF_COMMENT)
continue;
/* the first token in each line is the name, followed
by either the value or '{' */
name = tokenizer_next_word(&line, &error);
if (name == NULL) {
assert(*line != 0);
g_propagate_prefixed_error(error_r, error,
"line %i: ", count);
return false;
}
if (2 != argsMinusComment) {
g_error("improperly formatted config file at line %i:"
" %s\n", count, string);
/* get the definition of that option, and check the
"repeatable" flag */
entry = config_entry_get(name);
if (entry == NULL) {
g_set_error(error_r, config_quark(), 0,
"unrecognized parameter in config file at "
"line %i: %s\n", count, name);
return false;
}
entry = config_entry_get(array[0]);
if (entry == NULL)
g_error("unrecognized parameter in config file at "
"line %i: %s\n", count, string);
if (!(entry->mask & CONF_REPEATABLE_MASK) &&
entry->params != NULL) {
if (entry->params != NULL && !entry->repeatable) {
param = entry->params->data;
g_error("config parameter \"%s\" is first defined on "
"line %i and redefined on line %i\n",
array[0], param->line, count);
g_set_error(error_r, config_quark(), 0,
"config parameter \"%s\" is first defined "
"on line %i and redefined on line %i\n",
name, param->line, count);
return false;
}
if (entry->mask & CONF_BLOCK_MASK) {
if (0 != strcmp(array[1], CONF_BLOCK_BEGIN)) {
g_error("improperly formatted config file at "
"line %i: %s\n", count, string);
/* now parse the block or the value */
if (entry->block) {
/* it's a block, call config_read_block() */
if (*line != '{') {
g_set_error(error_r, config_quark(), 0,
"line %i: '{' expected", count);
return false;
}
line = g_strchug(line + 1);
if (*line != 0 && *line != CONF_COMMENT) {
g_set_error(error_r, config_quark(), 0,
"line %i: Unknown tokens after '{'",
count);
return false;
}
param = config_read_block(fp, &count, string, error_r);
if (param == NULL)
return false;
} else {
/* a string value */
value = tokenizer_next_string(&line, &error);
if (value == NULL) {
if (*line == 0)
g_set_error(error_r, config_quark(), 0,
"line %i: Value missing",
count);
else {
g_set_error(error_r, config_quark(), 0,
"line %i: %s", count,
error->message);
g_error_free(error);
}
return false;
}
if (*line != 0 && *line != CONF_COMMENT) {
g_set_error(error_r, config_quark(), 0,
"line %i: Unknown tokens after value",
count);
return false;
}
param = config_new_param(value, count);
}
param = config_read_block(fp, &count, string);
} else
param = config_new_param(array[1], count);
entry->params = g_slist_append(entry->params, param);
}
fclose(fp);
}
void
config_add_param(const char *name, struct config_param *param)
{
struct config_entry *entry = config_entry_get(name);
assert(entry != NULL);
entry->params = g_slist_append(entry->params, param);
return true;
}
struct config_param *
@@ -391,7 +473,7 @@ config_get_next_param(const char *name, const struct config_param * last)
return NULL;
param = node->data;
param->used = true;
return param;
}
@@ -417,13 +499,31 @@ config_get_path(const char *name)
path = parsePath(param->value);
if (path == NULL)
g_error("error parsing \"%s\" at line %i\n",
MPD_ERROR("error parsing \"%s\" at line %i\n",
name, param->line);
g_free(param->value);
return param->value = path;
}
unsigned
config_get_unsigned(const char *name, unsigned default_value)
{
const struct config_param *param = config_get_param(name);
long value;
char *endptr;
if (param == NULL)
return default_value;
value = strtol(param->value, &endptr, 0);
if (*endptr != 0 || value < 0)
MPD_ERROR("Not a valid non-negative number in line %i",
param->line);
return (unsigned)value;
}
unsigned
config_get_positive(const char *name, unsigned default_value)
{
@@ -436,10 +536,10 @@ config_get_positive(const char *name, unsigned default_value)
value = strtol(param->value, &endptr, 0);
if (*endptr != 0)
g_error("Not a valid number in line %i", param->line);
MPD_ERROR("Not a valid number in line %i", param->line);
if (value <= 0)
g_error("Not a positive number in line %i", param->line);
MPD_ERROR("Not a positive number in line %i", param->line);
return (unsigned)value;
}
@@ -447,43 +547,35 @@ config_get_positive(const char *name, unsigned default_value)
struct block_param *
config_get_block_param(const struct config_param * param, const char *name)
{
struct block_param *ret = NULL;
if (param == NULL)
return NULL;
for (unsigned i = 0; i < param->num_block_params; i++) {
if (0 == strcmp(name, param->block_params[i].name)) {
if (ret) {
g_warning("\"%s\" first defined on line %i, and "
"redefined on line %i\n", name,
ret->line, param->block_params[i].line);
}
ret = param->block_params + i;
struct block_param *bp = &param->block_params[i];
bp->used = true;
return bp;
}
}
return ret;
return NULL;
}
bool config_get_bool(const char *name, bool default_value)
{
const struct config_param *param = config_get_param(name);
int value;
bool success, value;
if (param == NULL)
return default_value;
value = get_bool(param->value);
if (value == CONF_BOOL_INVALID)
g_error("%s is not a boolean value (yes, true, 1) or "
success = get_bool(param->value, &value);
if (!success)
MPD_ERROR("%s is not a boolean value (yes, true, 1) or "
"(no, false, 0) on line %i\n",
name, param->line);
if (value == CONF_BOOL_UNSET)
return default_value;
return !!value;
return value;
}
const char *
@@ -511,10 +603,10 @@ config_get_block_unsigned(const struct config_param *param, const char *name,
value = strtol(bp->value, &endptr, 0);
if (*endptr != 0)
g_error("Not a valid number in line %i", bp->line);
MPD_ERROR("Not a valid number in line %i", bp->line);
if (value < 0)
g_error("Not a positive number in line %i", bp->line);
MPD_ERROR("Not a positive number in line %i", bp->line);
return (unsigned)value;
}
@@ -524,19 +616,16 @@ config_get_block_bool(const struct config_param *param, const char *name,
bool default_value)
{
struct block_param *bp = config_get_block_param(param, name);
int value;
bool success, value;
if (bp == NULL)
return default_value;
value = get_bool(bp->value);
if (value == CONF_BOOL_INVALID)
g_error("%s is not a boolean value (yes, true, 1) or "
success = get_bool(bp->value, &value);
if (!success)
MPD_ERROR("%s is not a boolean value (yes, true, 1) or "
"(no, false, 0) on line %i\n",
name, bp->line);
if (value == CONF_BOOL_UNSET)
return default_value;
return !!value;
return value;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2003-2009 The Music Player Daemon Project
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -30,10 +30,10 @@
#define CONF_DB_FILE "db_file"
#define CONF_STICKER_FILE "sticker_file"
#define CONF_LOG_FILE "log_file"
#define CONF_ERROR_FILE "error_file"
#define CONF_PID_FILE "pid_file"
#define CONF_STATE_FILE "state_file"
#define CONF_USER "user"
#define CONF_GROUP "group"
#define CONF_BIND_TO_ADDRESS "bind_to_address"
#define CONF_PORT "port"
#define CONF_LOG_LEVEL "log_level"
@@ -42,12 +42,13 @@
#define CONF_PASSWORD "password"
#define CONF_DEFAULT_PERMS "default_permissions"
#define CONF_AUDIO_OUTPUT "audio_output"
#define CONF_AUDIO_FILTER "filter"
#define CONF_AUDIO_OUTPUT_FORMAT "audio_output_format"
#define CONF_MIXER_TYPE "mixer_type"
#define CONF_MIXER_DEVICE "mixer_device"
#define CONF_MIXER_CONTROL "mixer_control"
#define CONF_REPLAYGAIN "replaygain"
#define CONF_REPLAYGAIN_PREAMP "replaygain_preamp"
#define CONF_REPLAYGAIN_MISSING_PREAMP "replaygain_missing_preamp"
#define CONF_REPLAYGAIN_LIMIT "replaygain_limit"
#define CONF_VOLUME_NORMALIZATION "volume_normalization"
#define CONF_SAMPLERATE_CONVERTER "samplerate_converter"
#define CONF_AUDIO_BUFFER_SIZE "audio_buffer_size"
@@ -68,17 +69,25 @@
#define CONF_DECODER "decoder"
#define CONF_INPUT "input"
#define CONF_GAPLESS_MP3_PLAYBACK "gapless_mp3_playback"
#define CONF_BOOL_UNSET -1
#define CONF_BOOL_INVALID -2
#define CONF_PLAYLIST_PLUGIN "playlist_plugin"
#define CONF_AUTO_UPDATE "auto_update"
#define CONF_AUTO_UPDATE_DEPTH "auto_update_depth"
#define DEFAULT_PLAYLIST_MAX_LENGTH (1024*16)
#define DEFAULT_PLAYLIST_SAVE_ABSOLUTE_PATHS false
#define MAX_FILTER_CHAIN_LENGTH 255
struct block_param {
char *name;
char *value;
int line;
/**
* This flag is false when nobody has queried the value of
* this option yet.
*/
bool used;
};
struct config_param {
@@ -87,31 +96,57 @@ struct config_param {
struct block_param *block_params;
unsigned num_block_params;
/**
* This flag is false when nobody has queried the value of
* this option yet.
*/
bool used;
};
/**
* A GQuark for GError instances, resulting from malformed
* configuration.
*/
static inline GQuark
config_quark(void)
{
return g_quark_from_static_string("config");
}
void config_global_init(void);
void config_global_finish(void);
void config_read_file(const char *file);
/**
* Adds a new configuration parameter. The name must be registered
* with registerConfigParam().
* Call this function after all configuration has been evaluated. It
* checks for unused parameters, and logs warnings.
*/
void
config_add_param(const char *name, struct config_param *param);
void config_global_check(void);
bool
config_read_file(const char *file, GError **error_r);
/* don't free the returned value
set _last_ to NULL to get first entry */
G_GNUC_PURE
struct config_param *
config_get_next_param(const char *name, const struct config_param *last);
G_GNUC_PURE
static inline struct config_param *
config_get_param(const char *name)
{
return config_get_next_param(name, NULL);
}
/* Note on G_GNUC_PURE: Some of the functions declared pure are not
really pure in strict sense. They have side effect such that they
validate parameter's value and signal an error if it's invalid.
However, if the argument was already validated or we don't care
about the argument at all, this may be ignored so in the end, we
should be fine with calling those functions pure. */
G_GNUC_PURE
const char *
config_get_string(const char *name, const char *default_value);
@@ -120,17 +155,31 @@ config_get_string(const char *name, const char *default_value);
* absolute path. If there is a tilde prefix, it is expanded. Aborts
* MPD if the path is not a valid absolute path.
*/
/* We lie here really. This function is not pure as it has side
effects -- it parse the value and creates new string freeing
previous one. However, because this works the very same way each
time (ie. from the outside it appears as if function had no side
effects) we should be in the clear declaring it pure. */
G_GNUC_PURE
const char *
config_get_path(const char *name);
G_GNUC_PURE
unsigned
config_get_unsigned(const char *name, unsigned default_value);
G_GNUC_PURE
unsigned
config_get_positive(const char *name, unsigned default_value);
G_GNUC_PURE
struct block_param *
config_get_block_param(const struct config_param *param, const char *name);
G_GNUC_PURE
bool config_get_bool(const char *name, bool default_value);
G_GNUC_PURE
const char *
config_get_block_string(const struct config_param *param, const char *name,
const char *default_value);
@@ -142,10 +191,12 @@ config_dup_block_string(const struct config_param *param, const char *name,
return g_strdup(config_get_block_string(param, name, default_value));
}
G_GNUC_PURE
unsigned
config_get_block_unsigned(const struct config_param *param, const char *name,
unsigned default_value);
G_GNUC_PURE
bool
config_get_block_bool(const struct config_param *param, const char *name,
bool default_value);
@@ -153,8 +204,8 @@ config_get_block_bool(const struct config_param *param, const char *name,
struct config_param *
config_new_param(const char *value, int line);
void
config_add_block_param(struct config_param *param, const char *name,
const char *value, int line);
bool
config_add_block_param(struct config_param * param, const char *name,
const char *value, int line, GError **error_r);
#endif

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2003-2009 The Music Player Daemon Project
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,6 +17,7 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h"
#include "crossfade.h"
#include "pcm_mix.h"
#include "chunk.h"
@@ -25,73 +26,112 @@
#include <assert.h>
#include <string.h>
#include <stdlib.h>
#include <glib.h>
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "crossfade"
#ifdef G_OS_WIN32
static char *
strtok_r(char *str, const char *delim, G_GNUC_UNUSED char **saveptr)
{
return strtok(str, delim);
}
#endif
static float mixramp_interpolate(char *ramp_list, float required_db)
{
float db, secs, last_db = nan(""), last_secs = 0;
char *ramp_str, *save_str = NULL;
/* ramp_list is a string of pairs of dBs and seconds that describe the
* volume profile. Delimiters are semi-colons between pairs and spaces
* between the dB and seconds of a pair.
* The dB values must be monotonically increasing for this to work. */
while (1) {
/* Parse the dB tokens out of the input string. */
ramp_str = strtok_r(ramp_list, " ", &save_str);
/* Tell strtok to continue next time round. */
ramp_list = NULL;
/* Parse the dB value. */
if (NULL == ramp_str) {
return nan("");
}
db = (float)atof(ramp_str);
/* Parse the time. */
ramp_str = strtok_r(NULL, ";", &save_str);
if (NULL == ramp_str) {
return nan("");
}
secs = (float)atof(ramp_str);
/* Check for exact match. */
if (db == required_db) {
return secs;
}
/* Save if too quiet. */
if (db < required_db) {
last_db = db;
last_secs = secs;
continue;
}
/* If required db < any stored value, use the least. */
if (isnan(last_db)) {
return secs;
}
/* Finally, interpolate linearly. */
secs = last_secs + (required_db - last_db) * (secs - last_secs) / (db - last_db);
return secs;
}
}
unsigned cross_fade_calc(float duration, float total_time,
float mixramp_db, float mixramp_delay,
float replay_gain_db, float replay_gain_prev_db,
char *mixramp_start, char *mixramp_prev_end,
const struct audio_format *af,
const struct audio_format *old_format,
unsigned max_chunks)
{
unsigned int chunks;
unsigned int chunks = 0;
float chunks_f;
float mixramp_overlap;
if (duration <= 0 || duration >= total_time ||
if (duration < 0 || duration >= total_time ||
/* we can't crossfade when the audio formats are different */
!audio_format_equals(af, old_format))
return 0;
assert(duration > 0);
assert(af->bits > 0);
assert(af->channels > 0);
assert(af->sample_rate > 0);
assert(duration >= 0);
assert(audio_format_valid(af));
chunks = audio_format_time_to_size(af) / CHUNK_SIZE;
chunks = (chunks * duration + 0.5);
chunks_f = (float)audio_format_time_to_size(af) / (float)CHUNK_SIZE;
if (chunks > max_chunks)
if (isnan(mixramp_delay) || !(mixramp_start) || !(mixramp_prev_end)) {
chunks = (chunks_f * duration + 0.5);
} else {
/* Calculate mixramp overlap. */
mixramp_overlap = mixramp_interpolate(mixramp_start, mixramp_db - replay_gain_db)
+ mixramp_interpolate(mixramp_prev_end, mixramp_db - replay_gain_prev_db);
if (!isnan(mixramp_overlap) && (mixramp_delay <= mixramp_overlap)) {
chunks = (chunks_f * (mixramp_overlap - mixramp_delay));
g_debug("will overlap %d chunks, %fs", chunks,
mixramp_overlap - mixramp_delay);
}
}
if (chunks > max_chunks) {
chunks = max_chunks;
g_warning("audio_buffer_size too small for computed MixRamp overlap");
}
return chunks;
}
void cross_fade_apply(struct music_chunk *a, const struct music_chunk *b,
const struct audio_format *format,
unsigned int current_chunk, unsigned int num_chunks)
{
size_t size;
assert(a != NULL);
assert(b != NULL);
assert(a->length == 0 || b->length == 0 ||
audio_format_equals(&a->audio_format, &b->audio_format));
assert(current_chunk <= num_chunks);
if (a->tag == NULL && b->tag != NULL)
/* merge the tag into the destination chunk */
a->tag = tag_dup(b->tag);
size = b->length > a->length
? a->length
: b->length;
pcm_mix(a->data,
b->data,
size,
format,
((float)current_chunk) / num_chunks);
if (b->length > a->length) {
/* the second buffer is larger than the first one:
there is unmixed rest at the end. Copy it over.
The output buffer API guarantees that there is
enough room in a->data. */
#ifndef NDEBUG
if (a->length == 0)
a->audio_format = b->audio_format;
#endif
memcpy(a->data + a->length,
b->data + a->length,
b->length - a->length);
a->length = b->length;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2003-2009 The Music Player Daemon Project
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -28,6 +28,12 @@ struct music_chunk;
*
* @param duration the requested crossfade duration
* @param total_time total_time the duration of the new song
* @param mixramp_db the current mixramp_db setting
* @param mixramp_delay the current mixramp_delay setting
* @param replay_gain_db the ReplayGain adjustment used for this song
* @param replay_gain_prev_db the ReplayGain adjustment used on the last song
* @param mixramp_start the next songs mixramp_start tag
* @param mixramp_prev_end the last songs mixramp_end setting
* @param af the audio format of the new song
* @param old_format the audio format of the current song
* @param max_chunks the maximum number of chunks
@@ -35,22 +41,11 @@ struct music_chunk;
* should be disabled for this song change
*/
unsigned cross_fade_calc(float duration, float total_time,
float mixramp_db, float mixramp_delay,
float replay_gain_db, float replay_gain_prev_db,
char *mixramp_start, char *mixramp_prev_end,
const struct audio_format *af,
const struct audio_format *old_format,
unsigned max_chunks);
/**
* Applies cross fading to two chunks, i.e. mixes these chunks.
* Internally, this calls pcm_mix().
*
* @param a the chunk in the current song (and the destination chunk)
* @param b the according chunk in the new song
* @param format the audio format of both chunks (must be the same)
* @param current_chunk the relative index of the current chunk
* @param num_chunks the number of chunks used for cross fading
*/
void cross_fade_apply(struct music_chunk *a, const struct music_chunk *b,
const struct audio_format *format,
unsigned int current_chunk, unsigned int num_chunks);
#endif

View File

@@ -1,76 +1,78 @@
#include "config.h"
#include "cue_tag.h"
#include "tag.h"
static struct tag*
cue_tag_cd(struct Cdtext* cdtext, struct Rem* rem)
#include <libcue/libcue.h>
#include <assert.h>
static struct tag *
cue_tag_cd(struct Cdtext *cdtext, struct Rem *rem)
{
char* tmp = NULL;
struct tag* tag = NULL;
struct tag *tag;
char *tmp;
//if (cdtext == NULL)
//return NULL;
assert(cdtext != NULL);
tag = tag_new();
tag_begin_add(tag);
{ /* TAG_ITEM_ALBUM_ARTIST */
/* TAG_ALBUM_ARTIST */
if ((tmp = cdtext_get(PTI_PERFORMER, cdtext)) != NULL)
tag_add_item(tag, TAG_ITEM_ALBUM_ARTIST, tmp);
tag_add_item(tag, TAG_ALBUM_ARTIST, tmp);
else if ((tmp = cdtext_get(PTI_SONGWRITER, cdtext)) != NULL)
tag_add_item(tag, TAG_ITEM_ALBUM_ARTIST, tmp);
tag_add_item(tag, TAG_ALBUM_ARTIST, tmp);
else if ((tmp = cdtext_get(PTI_COMPOSER, cdtext)) != NULL)
tag_add_item(tag, TAG_ITEM_ALBUM_ARTIST, tmp);
tag_add_item(tag, TAG_ALBUM_ARTIST, tmp);
else if ((tmp = cdtext_get(PTI_ARRANGER, cdtext)) != NULL)
tag_add_item(tag, TAG_ITEM_ALBUM_ARTIST, tmp);
/* TAG_ITEM_ALBUM_ARTIST */ }
tag_add_item(tag, TAG_ALBUM_ARTIST, tmp);
{ /* TAG_ITEM_ARTIST */
/* TAG_ARTIST */
if ((tmp = cdtext_get(PTI_PERFORMER, cdtext)) != NULL)
tag_add_item(tag, TAG_ITEM_ARTIST, tmp);
tag_add_item(tag, TAG_ARTIST, tmp);
else if ((tmp = cdtext_get(PTI_SONGWRITER, cdtext)) != NULL)
tag_add_item(tag, TAG_ITEM_ARTIST, tmp);
tag_add_item(tag, TAG_ARTIST, tmp);
else if ((tmp = cdtext_get(PTI_COMPOSER, cdtext)) != NULL)
tag_add_item(tag, TAG_ITEM_ARTIST, tmp);
tag_add_item(tag, TAG_ARTIST, tmp);
else if ((tmp = cdtext_get(PTI_ARRANGER, cdtext)) != NULL)
tag_add_item(tag, TAG_ITEM_ARTIST, tmp);
/* TAG_ITEM_ARTIST */ }
tag_add_item(tag, TAG_ARTIST, tmp);
/* TAG_ITEM_PERFORMER */
/* TAG_PERFORMER */
if ((tmp = cdtext_get(PTI_PERFORMER, cdtext)) != NULL)
tag_add_item(tag, TAG_ITEM_PERFORMER, tmp);
tag_add_item(tag, TAG_PERFORMER, tmp);
/* TAG_ITEM_COMPOSER */
/* TAG_COMPOSER */
if ((tmp = cdtext_get(PTI_COMPOSER, cdtext)) != NULL)
tag_add_item(tag, TAG_ITEM_COMPOSER, tmp);
tag_add_item(tag, TAG_COMPOSER, tmp);
/* TAG_ITEM_ALBUM */
/* TAG_ALBUM */
if ((tmp = cdtext_get(PTI_TITLE, cdtext)) != NULL)
tag_add_item(tag, TAG_ITEM_ALBUM, tmp);
tag_add_item(tag, TAG_ALBUM, tmp);
/* TAG_ITEM_GENRE */
/* TAG_GENRE */
if ((tmp = cdtext_get(PTI_GENRE, cdtext)) != NULL)
tag_add_item(tag, TAG_ITEM_GENRE, tmp);
tag_add_item(tag, TAG_GENRE, tmp);
/* TAG_ITEM_DATE */
/* TAG_DATE */
if ((tmp = rem_get(REM_DATE, rem)) != NULL)
tag_add_item(tag, TAG_ITEM_DATE, tmp);
tag_add_item(tag, TAG_DATE, tmp);
/* TAG_ITEM_COMMENT */
/* TAG_COMMENT */
if ((tmp = cdtext_get(PTI_MESSAGE, cdtext)) != NULL)
tag_add_item(tag, TAG_ITEM_COMMENT, tmp);
tag_add_item(tag, TAG_COMMENT, tmp);
/* TAG_ITEM_DISC */
/* TAG_DISC */
if ((tmp = cdtext_get(PTI_DISC_ID, cdtext)) != NULL)
tag_add_item(tag, TAG_ITEM_DISC, tmp);
tag_add_item(tag, TAG_DISC, tmp);
/* stream name, usually empty
* tag_add_item(tag, TAG_ITEM_NAME,);
* tag_add_item(tag, TAG_NAME,);
*/
/* REM MUSICBRAINZ entry?
@@ -82,175 +84,152 @@ cue_tag_cd(struct Cdtext* cdtext, struct Rem* rem)
tag_end_add(tag);
if (tag != NULL)
{
if (tag_is_empty(tag))
{
if (tag_is_empty(tag)) {
tag_free(tag);
return NULL;
}
else
return tag;
}
else
return NULL;
}
static struct tag*
cue_tag_track(struct Cdtext* cdtext, struct Rem* rem)
static struct tag *
cue_tag_track(struct Cdtext *cdtext, struct Rem *rem)
{
char* tmp = NULL;
struct tag* tag = NULL;
struct tag *tag;
char *tmp;
//if (cdtext == NULL)
//return NULL;
assert(cdtext != NULL);
tag = tag_new();
tag_begin_add(tag);
{ /* TAG_ITEM_ARTIST */
/* TAG_ARTIST */
if ((tmp = cdtext_get(PTI_PERFORMER, cdtext)) != NULL)
tag_add_item(tag, TAG_ITEM_ARTIST, tmp);
tag_add_item(tag, TAG_ARTIST, tmp);
else if ((tmp = cdtext_get(PTI_SONGWRITER, cdtext)) != NULL)
tag_add_item(tag, TAG_ITEM_ARTIST, tmp);
tag_add_item(tag, TAG_ARTIST, tmp);
else if ((tmp = cdtext_get(PTI_COMPOSER, cdtext)) != NULL)
tag_add_item(tag, TAG_ITEM_ARTIST, tmp);
tag_add_item(tag, TAG_ARTIST, tmp);
else if ((tmp = cdtext_get(PTI_ARRANGER, cdtext)) != NULL)
tag_add_item(tag, TAG_ITEM_ARTIST, tmp);
/* TAG_ITEM_ARTIST */ }
tag_add_item(tag, TAG_ARTIST, tmp);
/* TAG_ITEM_TITLE */
/* TAG_TITLE */
if ((tmp = cdtext_get(PTI_TITLE, cdtext)) != NULL)
tag_add_item(tag, TAG_ITEM_TITLE, tmp);
tag_add_item(tag, TAG_TITLE, tmp);
/* TAG_ITEM_GENRE */
/* TAG_GENRE */
if ((tmp = cdtext_get(PTI_GENRE, cdtext)) != NULL)
tag_add_item(tag, TAG_ITEM_GENRE, tmp);
tag_add_item(tag, TAG_GENRE, tmp);
/* TAG_ITEM_DATE */
/* TAG_DATE */
if ((tmp = rem_get(REM_DATE, rem)) != NULL)
tag_add_item(tag, TAG_ITEM_DATE, tmp);
tag_add_item(tag, TAG_DATE, tmp);
/* TAG_ITEM_COMPOSER */
/* TAG_COMPOSER */
if ((tmp = cdtext_get(PTI_COMPOSER, cdtext)) != NULL)
tag_add_item(tag, TAG_ITEM_COMPOSER, tmp);
tag_add_item(tag, TAG_COMPOSER, tmp);
/* TAG_ITEM_PERFORMER */
/* TAG_PERFORMER */
if ((tmp = cdtext_get(PTI_PERFORMER, cdtext)) != NULL)
tag_add_item(tag, TAG_ITEM_PERFORMER, tmp);
tag_add_item(tag, TAG_PERFORMER, tmp);
/* TAG_ITEM_COMMENT */
/* TAG_COMMENT */
if ((tmp = cdtext_get(PTI_MESSAGE, cdtext)) != NULL)
tag_add_item(tag, TAG_ITEM_COMMENT, tmp);
tag_add_item(tag, TAG_COMMENT, tmp);
/* TAG_ITEM_DISC */
/* TAG_DISC */
if ((tmp = cdtext_get(PTI_DISC_ID, cdtext)) != NULL)
tag_add_item(tag, TAG_ITEM_DISC, tmp);
tag_add_item(tag, TAG_DISC, tmp);
tag_end_add(tag);
if (tag != NULL)
{
if (tag_is_empty(tag))
{
if (tag_is_empty(tag)) {
tag_free(tag);
return NULL;
}
else
return tag;
}
else
return NULL;
}
struct tag*
cue_tag_file( FILE* fp,
const unsigned int tnum)
struct tag *
cue_tag(struct Cd *cd, unsigned tnum)
{
struct tag* cd_tag = NULL;
struct tag* track_tag = NULL;
struct Cd* cd = NULL;
struct tag *cd_tag, *track_tag, *tag;
struct Track *track;
assert(cd != NULL);
track = cd_get_track(cd, tnum);
if (track == NULL)
return NULL;
/* tag from CDtext info */
cd_tag = cue_tag_cd(cd_get_cdtext(cd), cd_get_rem(cd));
/* tag from TRACKtext info */
track_tag = cue_tag_track(track_get_cdtext(track),
track_get_rem(track));
tag = tag_merge_replace(cd_tag, track_tag);
if (tag == NULL)
return NULL;
tag->time = track_get_length(track)
- track_get_index(track, 1)
+ track_get_zero_pre(track);
track = cd_get_track(cd, tnum + 1);
if (track != NULL)
tag->time += track_get_index(track, 1)
- track_get_zero_pre(track);
/* libcue returns the track duration in frames, and there are
75 frames per second; this formula rounds down */
tag->time = tag->time / 75;
return tag;
}
struct tag *
cue_tag_file(FILE *fp, unsigned tnum)
{
struct Cd *cd;
struct tag *tag;
assert(fp != NULL);
if (tnum > 256)
return NULL;
if (fp == NULL)
return NULL;
else
cd = cue_parse_file(fp);
if (cd == NULL)
return NULL;
else
{
/* tag from CDtext info */
cd_tag = cue_tag_cd( cd_get_cdtext(cd),
cd_get_rem(cd));
/* tag from TRACKtext info */
track_tag = cue_tag_track( track_get_cdtext( cd_get_track(cd, tnum)),
track_get_rem( cd_get_track(cd, tnum)));
tag = cue_tag(cd, tnum);
cd_delete(cd);
}
return tag_merge_replace(cd_tag, track_tag);
return tag;
}
struct tag*
cue_tag_string( char* str,
const unsigned int tnum)
struct tag *
cue_tag_string(const char *str, unsigned tnum)
{
struct tag* cd_tag = NULL;
struct tag* track_tag = NULL;
struct tag* merge_tag = NULL;
struct Cd* cd = NULL;
struct Cd *cd;
struct tag *tag;
assert(str != NULL);
if (tnum > 256)
return NULL;
if (str == NULL)
return NULL;
else
cd = cue_parse_string(str);
if (cd == NULL)
return NULL;
else
{
/* tag from CDtext info */
cd_tag = cue_tag_cd( cd_get_cdtext(cd),
cd_get_rem(cd));
/* tag from TRACKtext info */
track_tag = cue_tag_track( track_get_cdtext( cd_get_track(cd, tnum)),
track_get_rem( cd_get_track(cd, tnum)));
tag = cue_tag(cd, tnum);
cd_delete(cd);
}
if ((cd_tag != NULL) && (track_tag != NULL))
{
merge_tag = tag_merge(cd_tag, track_tag);
tag_free(cd_tag);
tag_free(track_tag);
return merge_tag;
}
else if (cd_tag != NULL)
{
return cd_tag;
}
else if (track_tag != NULL)
{
return track_tag;
}
else
return NULL;
return tag;
}

View File

@@ -1,20 +1,23 @@
#ifndef MPD_CUE_TAG_H
#define MPD_CUE_TAG_H
#include "config.h"
#include "check.h"
#ifdef HAVE_CUE /* libcue */
#include <libcue/libcue.h>
#include "../tag.h"
#include <stdio.h>
struct tag*
cue_tag_file( FILE*,
const unsigned int);
struct tag;
struct Cd;
struct tag*
cue_tag_string( char*,
const unsigned int);
struct tag *
cue_tag(struct Cd *cd, unsigned tnum);
struct tag *
cue_tag_file(FILE *file, unsigned tnum);
struct tag *
cue_tag_string(const char *str, unsigned tnum);
#endif /* libcue */
#endif

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2003-2009 The Music Player Daemon Project
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,6 +17,7 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h"
#include "daemon.h"
#include <glib.h>
@@ -45,78 +46,72 @@
static char *user_name;
/** the Unix user id which MPD runs as */
static uid_t user_uid;
static uid_t user_uid = (uid_t)-1;
/** the Unix group id which MPD runs as */
static gid_t user_gid;
static gid_t user_gid = (pid_t)-1;
/** the absolute path of the pidfile */
static char *pidfile;
#endif
/* whether "group" conf. option was given */
static bool had_group = false;
void
daemonize_kill(void)
{
#ifndef WIN32
FILE *fp;
int pid, ret;
if (pidfile == NULL)
g_error("no pid_file specified in the config file");
MPD_ERROR("no pid_file specified in the config file");
fp = fopen(pidfile, "r");
if (fp == NULL)
g_error("unable to open pid file \"%s\": %s",
MPD_ERROR("unable to open pid file \"%s\": %s",
pidfile, g_strerror(errno));
if (fscanf(fp, "%i", &pid) != 1) {
g_error("unable to read the pid from file \"%s\"",
MPD_ERROR("unable to read the pid from file \"%s\"",
pidfile);
}
fclose(fp);
ret = kill(pid, SIGTERM);
if (ret < 0)
g_error("unable to kill proccess %i: %s",
MPD_ERROR("unable to kill proccess %i: %s",
pid, g_strerror(errno));
exit(EXIT_SUCCESS);
#else
g_error("--kill is not available on WIN32");
#endif
}
void
daemonize_close_stdin(void)
{
int fd = open("/dev/null", O_RDONLY);
if (fd < 0)
close(STDIN_FILENO);
else if (fd != STDIN_FILENO) {
dup2(fd, STDIN_FILENO);
close(fd);
}
open("/dev/null", O_RDONLY);
}
void
daemonize_set_user(void)
{
#ifndef WIN32
if (user_name == NULL)
return;
/* get uid */
/* set gid */
if (user_gid != (gid_t)-1 && user_gid != getgid()) {
if (setgid(user_gid) == -1) {
g_error("cannot setgid for user \"%s\": %s",
user_name, g_strerror(errno));
MPD_ERROR("cannot setgid to %d: %s",
(int)user_gid, g_strerror(errno));
}
}
#ifdef _BSD_SOURCE
/* init suplementary groups
* (must be done before we change our uid)
*/
if (initgroups(user_name, user_gid) == -1) {
if (!had_group && initgroups(user_name, user_gid) == -1) {
g_warning("cannot init supplementary groups "
"of user \"%s\": %s",
user_name, g_strerror(errno));
@@ -124,50 +119,58 @@ daemonize_set_user(void)
#endif
/* set uid */
if (setuid(user_uid) == -1) {
g_error("cannot change to uid of user \"%s\": %s",
if (user_uid != (uid_t)-1 && user_uid != getuid() &&
setuid(user_uid) == -1) {
MPD_ERROR("cannot change to uid of user \"%s\": %s",
user_name, g_strerror(errno));
}
#endif
}
#ifndef G_OS_WIN32
static void
daemonize_detach(void)
{
pid_t pid;
/* flush all file handles before duplicating the buffers */
fflush(NULL);
#ifdef HAVE_DAEMON
if (daemon(0, 1))
MPD_ERROR("daemon() failed: %s", g_strerror(errno));
#elif defined(HAVE_FORK)
/* detach from parent process */
pid = fork();
if (pid < 0)
g_error("fork() failed: %s", g_strerror(errno));
if (pid > 0)
switch (fork()) {
case -1:
MPD_ERROR("fork() failed: %s", g_strerror(errno));
case 0:
break;
default:
/* exit the parent process */
_exit(EXIT_SUCCESS);
}
/* release the current working directory */
if (chdir("/") < 0)
g_error("problems changing to root directory");
MPD_ERROR("problems changing to root directory");
/* detach from the current session */
setsid();
#else
MPD_ERROR("no support for daemonizing");
#endif
g_debug("daemonized!");
}
#endif
void
daemonize(bool detach)
{
#ifndef WIN32
FILE *fp = NULL;
if (pidfile != NULL) {
@@ -176,7 +179,7 @@ daemonize(bool detach)
g_debug("opening pid file");
fp = fopen(pidfile, "w+");
if (!fp) {
g_error("could not create pid file \"%s\": %s",
MPD_ERROR("could not create pid file \"%s\": %s",
pidfile, g_strerror(errno));
}
}
@@ -189,47 +192,45 @@ daemonize(bool detach)
fprintf(fp, "%lu\n", (unsigned long)getpid());
fclose(fp);
}
#else
/* no daemonization on WIN32 */
(void)detach;
#endif
}
void
daemonize_init(const char *user, const char *_pidfile)
daemonize_init(const char *user, const char *group, const char *_pidfile)
{
#ifndef WIN32
if (user != NULL && strcmp(user, g_get_user_name()) != 0) {
struct passwd *pwd;
user_name = g_strdup(user);
pwd = getpwnam(user_name);
if (pwd == NULL)
g_error("no such user \"%s\"", user_name);
if (user) {
struct passwd *pwd = getpwnam(user);
if (!pwd)
MPD_ERROR("no such user \"%s\"", user);
user_uid = pwd->pw_uid;
user_gid = pwd->pw_gid;
user_name = g_strdup(user);
/* this is needed by libs such as arts */
g_setenv("HOME", pwd->pw_dir, true);
}
if (group) {
struct group *grp = grp = getgrnam(group);
if (!grp)
MPD_ERROR("no such group \"%s\"", group);
user_gid = grp->gr_gid;
had_group = true;
}
pidfile = g_strdup(_pidfile);
#else
(void)user;
(void)_pidfile;
#endif
}
void
daemonize_finish(void)
{
#ifndef WIN32
if (pidfile != NULL)
unlink(pidfile);
g_free(user_name);
g_free(pidfile);
#endif
}
#endif

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2003-2009 The Music Player Daemon Project
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -20,34 +20,72 @@
#ifndef DAEMON_H
#define DAEMON_H
#include "mpd_error.h"
#include <stdbool.h>
#ifndef WIN32
void
daemonize_init(const char *user, const char *pidfile);
daemonize_init(const char *user, const char *group, const char *pidfile);
#else
static inline void
daemonize_init(const char *user, const char *group, const char *pidfile)
{ (void)user; (void)group; (void)pidfile; }
#endif
#ifndef WIN32
void
daemonize_finish(void);
#else
static inline void
daemonize_finish(void)
{ /* nop */ }
#endif
/**
* Kill the MPD which is currently running, pid determined from the
* pid file.
*/
#ifndef WIN32
void
daemonize_kill(void);
#else
#include <glib.h>
static inline void
daemonize_kill(void)
{ MPD_ERROR("--kill is not available on WIN32"); }
#endif
/**
* Close stdin (fd 0) and re-open it as /dev/null.
*/
#ifndef WIN32
void
daemonize_close_stdin(void);
#else
static inline void
daemonize_close_stdin(void) {}
#endif
/**
* Change to the configured Unix user.
*/
#ifndef WIN32
void
daemonize_set_user(void);
#else
static inline void
daemonize_set_user(void)
{ /* nop */ }
#endif
#ifndef WIN32
void
daemonize(bool detach);
#else
static inline void
daemonize(bool detach)
{ (void)detach; }
#endif
#endif

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2003-2009 The Music Player Daemon Project
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,13 +17,16 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h"
#include "database.h"
#include "directory.h"
#include "directory_save.h"
#include "song.h"
#include "path.h"
#include "stats.h"
#include "config.h"
#include "text_file.h"
#include "tag.h"
#include "tag_internal.h"
#include <glib.h>
@@ -40,8 +43,14 @@
#define DIRECTORY_INFO_BEGIN "info_begin"
#define DIRECTORY_INFO_END "info_end"
#define DB_FORMAT_PREFIX "format: "
#define DIRECTORY_MPD_VERSION "mpd_version: "
#define DIRECTORY_FS_CHARSET "fs_charset: "
#define DB_TAG_PREFIX "tag: "
enum {
DB_FORMAT = 1,
};
static char *database_path;
@@ -230,20 +239,27 @@ db_save(void)
return false;
}
/* block signals when writing the db so we don't get a corrupted db */
fprintf(fp, "%s\n", DIRECTORY_INFO_BEGIN);
fprintf(fp, DB_FORMAT_PREFIX "%u\n", DB_FORMAT);
fprintf(fp, "%s%s\n", DIRECTORY_MPD_VERSION, VERSION);
fprintf(fp, "%s%s\n", DIRECTORY_FS_CHARSET, path_get_fs_charset());
for (unsigned i = 0; i < TAG_NUM_OF_ITEM_TYPES; ++i)
if (!ignore_tag_items[i])
fprintf(fp, DB_TAG_PREFIX "%s\n", tag_item_names[i]);
fprintf(fp, "%s\n", DIRECTORY_INFO_END);
if (directory_save(fp, music_root) < 0) {
directory_save(fp, music_root);
if (ferror(fp)) {
g_warning("Failed to write to database file: %s",
strerror(errno));
while (fclose(fp) && errno == EINTR);
fclose(fp);
return false;
}
while (fclose(fp) && errno == EINTR);
fclose(fp);
if (stat(database_path, &st) == 0)
database_mtime = st.st_mtime;
@@ -256,64 +272,64 @@ db_load(GError **error)
{
FILE *fp = NULL;
struct stat st;
char buffer[100];
GString *buffer = g_string_sized_new(1024);
char *line;
int format = 0;
bool found_charset = false, found_version = false;
bool success;
bool tags[TAG_NUM_OF_ITEM_TYPES];
assert(database_path != NULL);
assert(music_root != NULL);
if (!music_root)
music_root = directory_new("", NULL);
while (!(fp = fopen(database_path, "r")) && errno == EINTR) ;
fp = fopen(database_path, "r");
if (fp == NULL) {
g_set_error(error, db_quark(), errno,
"Failed to open database file \"%s\": %s",
database_path, strerror(errno));
g_string_free(buffer, true);
return false;
}
/* get initial info */
if (!fgets(buffer, sizeof(buffer), fp)) {
fclose(fp);
g_set_error(error, db_quark(), 0, "Unexpected end of file");
return false;
}
g_strchomp(buffer);
if (0 != strcmp(DIRECTORY_INFO_BEGIN, buffer)) {
line = read_text_line(fp, buffer);
if (line == NULL || strcmp(DIRECTORY_INFO_BEGIN, line) != 0) {
fclose(fp);
g_set_error(error, db_quark(), 0, "Database corrupted");
g_string_free(buffer, true);
return false;
}
while (fgets(buffer, sizeof(buffer), fp) &&
!g_str_has_prefix(buffer, DIRECTORY_INFO_END)) {
g_strchomp(buffer);
memset(tags, false, sizeof(tags));
if (g_str_has_prefix(buffer, DIRECTORY_MPD_VERSION)) {
while ((line = read_text_line(fp, buffer)) != NULL &&
strcmp(line, DIRECTORY_INFO_END) != 0) {
if (g_str_has_prefix(line, DB_FORMAT_PREFIX)) {
format = atoi(line + sizeof(DB_FORMAT_PREFIX) - 1);
} else if (g_str_has_prefix(line, DIRECTORY_MPD_VERSION)) {
if (found_version) {
fclose(fp);
g_set_error(error, db_quark(), 0,
"Duplicate version line");
g_string_free(buffer, true);
return false;
}
found_version = true;
} else if (g_str_has_prefix(buffer, DIRECTORY_FS_CHARSET)) {
} else if (g_str_has_prefix(line, DIRECTORY_FS_CHARSET)) {
const char *new_charset, *old_charset;
if (found_charset) {
fclose(fp);
g_set_error(error, db_quark(), 0,
"Duplicate charset line");
g_string_free(buffer, true);
return false;
}
found_charset = true;
new_charset = &(buffer[strlen(DIRECTORY_FS_CHARSET)]);
new_charset = line + sizeof(DIRECTORY_FS_CHARSET) - 1;
old_charset = path_get_fs_charset();
if (old_charset != NULL
&& strcmp(new_charset, old_charset)) {
@@ -323,20 +339,51 @@ db_load(GError **error)
"\"%s\" instead of \"%s\"; "
"discarding database file",
new_charset, old_charset);
g_string_free(buffer, true);
return false;
}
} else if (g_str_has_prefix(line, DB_TAG_PREFIX)) {
const char *name = line + sizeof(DB_TAG_PREFIX) - 1;
enum tag_type tag = tag_name_parse(name);
if (tag == TAG_NUM_OF_ITEM_TYPES) {
g_set_error(error, db_quark(), 0,
"Unrecognized tag '%s', "
"discarding database file",
name);
return false;
}
tags[tag] = true;
} else {
fclose(fp);
g_set_error(error, db_quark(), 0,
"Malformed line: %s", buffer);
"Malformed line: %s", line);
g_string_free(buffer, true);
return false;
}
}
if (format != DB_FORMAT) {
g_set_error(error, db_quark(), 0,
"Database format mismatch, "
"discarding database file");
return false;
}
for (unsigned i = 0; i < TAG_NUM_OF_ITEM_TYPES; ++i) {
if (!ignore_tag_items[i] && !tags[i]) {
g_set_error(error, db_quark(), 0,
"Tag list mismatch, "
"discarding database file");
return false;
}
}
g_debug("reading DB");
success = directory_load(fp, music_root, error);
while (fclose(fp) && errno == EINTR) ;
success = directory_load(fp, music_root, buffer, error);
g_string_free(buffer, true);
fclose(fp);
if (!success)
return false;

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2003-2009 The Music Player Daemon Project
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2003-2009 The Music Player Daemon Project
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,6 +17,7 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h"
#include "dbUtils.h"
#include "locate.h"
#include "directory.h"
@@ -59,7 +60,7 @@ static int
printSongInDirectory(struct song *song, G_GNUC_UNUSED void *data)
{
struct client *client = data;
song_print_url(client, song);
song_print_uri(client, song);
return 0;
}
@@ -74,7 +75,7 @@ searchInDirectory(struct song *song, void *_data)
struct search_data *data = _data;
if (locate_song_search(song, data->criteria))
return song_print_info(data->client, song);
song_print_info(data->client, song);
return 0;
}
@@ -104,7 +105,7 @@ findInDirectory(struct song *song, void *_data)
struct search_data *data = _data;
if (locate_song_match(song, data->criteria))
return song_print_info(data->client, song);
song_print_info(data->client, song);
return 0;
}
@@ -134,8 +135,7 @@ searchStatsInDirectory(struct song *song, void *data)
if (locate_song_match(song, stats->criteria)) {
stats->numberOfSongs++;
if (song->tag->time > 0)
stats->playTime += song->tag->time;
stats->playTime += song_get_duration(song);
}
return 0;
@@ -168,7 +168,7 @@ int printAllIn(struct client *client, const char *name)
static int
directoryAddSongToPlaylist(struct song *song, G_GNUC_UNUSED void *data)
{
return addSongToPlaylist(&g_playlist, song, NULL);
return playlist_append_song(&g_playlist, song, NULL);
}
struct add_data {
@@ -199,6 +199,28 @@ int addAllInToStoredPlaylist(const char *name, const char *utf8file)
return db_walk(name, directoryAddSongToStoredPlaylist, NULL, &data);
}
static int
findAddInDirectory(struct song *song, void *_data)
{
struct search_data *data = _data;
if (locate_song_match(song, data->criteria))
return directoryAddSongToPlaylist(song, data);
return 0;
}
int findAddIn(struct client *client, const char *name,
const struct locate_item_list *criteria)
{
struct search_data data;
data.client = client;
data.criteria = criteria;
return db_walk(name, findAddInDirectory, NULL, &data);
}
static int
directoryPrintSongInfo(struct song *song, void *data)
{
@@ -237,7 +259,7 @@ visitTag(struct client *client, struct strset *set,
bool found = false;
if (tagType == LOCATE_TAG_FILE_TYPE) {
song_print_url(client, song);
song_print_uri(client, song);
return;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2003-2009 The Music Player Daemon Project
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -39,6 +39,10 @@ int
findSongsIn(struct client *client, const char *name,
const struct locate_item_list *criteria);
int
findAddIn(struct client *client, const char *name,
const struct locate_item_list *criteria);
int
searchStatsForSongsIn(struct client *client, const char *name,
const struct locate_item_list *criteria);

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2003-2009 The Music Player Daemon Project
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -21,7 +21,11 @@
* Common data structures and functions used by FLAC and OggFLAC
*/
#include "config.h"
#include "_flac_common.h"
#include "flac_metadata.h"
#include "flac_pcm.h"
#include "audio_check.h"
#include <glib.h>
@@ -31,186 +35,104 @@ void
flac_data_init(struct flac_data *data, struct decoder * decoder,
struct input_stream *input_stream)
{
data->time = 0;
pcm_buffer_init(&data->buffer);
data->unsupported = false;
data->initialized = false;
data->total_frames = 0;
data->first_frame = 0;
data->next_frame = 0;
data->position = 0;
data->bit_rate = 0;
data->decoder = decoder;
data->input_stream = input_stream;
data->replay_gain_info = NULL;
data->tag = NULL;
}
static void
flac_find_float_comment(const FLAC__StreamMetadata *block,
const char *cmnt, float *fl, bool *found_r)
{
int offset;
size_t pos;
int len;
unsigned char tmp, *p;
offset = FLAC__metadata_object_vorbiscomment_find_entry_from(block, 0,
cmnt);
if (offset < 0)
return;
pos = strlen(cmnt) + 1; /* 1 is for '=' */
len = block->data.vorbis_comment.comments[offset].length - pos;
if (len <= 0)
return;
p = &block->data.vorbis_comment.comments[offset].entry[pos];
tmp = p[len];
p[len] = '\0';
*fl = (float)atof((char *)p);
p[len] = tmp;
*found_r = true;
}
static void
flac_parse_replay_gain(const FLAC__StreamMetadata *block,
struct flac_data *data)
{
bool found = false;
if (data->replay_gain_info)
replay_gain_info_free(data->replay_gain_info);
data->replay_gain_info = replay_gain_info_new();
flac_find_float_comment(block, "replaygain_album_gain",
&data->replay_gain_info->tuples[REPLAY_GAIN_ALBUM].gain,
&found);
flac_find_float_comment(block, "replaygain_album_peak",
&data->replay_gain_info->tuples[REPLAY_GAIN_ALBUM].peak,
&found);
flac_find_float_comment(block, "replaygain_track_gain",
&data->replay_gain_info->tuples[REPLAY_GAIN_TRACK].gain,
&found);
flac_find_float_comment(block, "replaygain_track_peak",
&data->replay_gain_info->tuples[REPLAY_GAIN_TRACK].peak,
&found);
if (!found) {
replay_gain_info_free(data->replay_gain_info);
data->replay_gain_info = NULL;
}
}
/**
* Checks if the specified name matches the entry's name, and if yes,
* returns the comment value (not null-temrinated).
*/
static const char *
flac_comment_value(const FLAC__StreamMetadata_VorbisComment_Entry *entry,
const char *name, const char *char_tnum, size_t *length_r)
{
size_t name_length = strlen(name);
size_t char_tnum_length = 0;
const char *comment = (const char*)entry->entry;
if (entry->length <= name_length ||
g_ascii_strncasecmp(comment, name, name_length) != 0)
return NULL;
if (char_tnum != NULL) {
char_tnum_length = strlen(char_tnum);
if (entry->length > name_length + char_tnum_length + 2 &&
comment[name_length] == '[' &&
g_ascii_strncasecmp(comment + name_length + 1,
char_tnum, char_tnum_length) == 0 &&
comment[name_length + char_tnum_length + 1] == ']')
name_length = name_length + char_tnum_length + 2;
else if (entry->length > name_length + char_tnum_length &&
g_ascii_strncasecmp(comment + name_length,
char_tnum, char_tnum_length) == 0)
name_length = name_length + char_tnum_length;
}
if (comment[name_length] == '=') {
*length_r = entry->length - name_length - 1;
return comment + name_length + 1;
}
return NULL;
}
/**
* Check if the comment's name equals the passed name, and if so, copy
* the comment value into the tag.
*/
static bool
flac_copy_comment(struct tag *tag,
const FLAC__StreamMetadata_VorbisComment_Entry *entry,
const char *name, enum tag_type tag_type,
const char *char_tnum)
{
const char *value;
size_t value_length;
value = flac_comment_value(entry, name, char_tnum, &value_length);
if (value != NULL) {
tag_add_item_n(tag, tag_type, value, value_length);
return true;
}
return false;
}
/* tracknumber is used in VCs, MPD uses "track" ..., all the other
* tag names match */
static const char *VORBIS_COMMENT_TRACK_KEY = "tracknumber";
static const char *VORBIS_COMMENT_DISC_KEY = "discnumber";
static void
flac_parse_comment(struct tag *tag, const char *char_tnum,
const FLAC__StreamMetadata_VorbisComment_Entry *entry)
{
assert(tag != NULL);
if (flac_copy_comment(tag, entry, VORBIS_COMMENT_TRACK_KEY,
TAG_ITEM_TRACK, char_tnum) ||
flac_copy_comment(tag, entry, VORBIS_COMMENT_DISC_KEY,
TAG_ITEM_DISC, char_tnum) ||
flac_copy_comment(tag, entry, "album artist",
TAG_ITEM_ALBUM_ARTIST, char_tnum))
return;
for (unsigned i = 0; i < TAG_NUM_OF_ITEM_TYPES; ++i)
if (flac_copy_comment(tag, entry,
tag_item_names[i], i, char_tnum))
return;
}
void
flac_vorbis_comments_to_tag(struct tag *tag, const char *char_tnum,
const FLAC__StreamMetadata *block)
flac_data_deinit(struct flac_data *data)
{
FLAC__StreamMetadata_VorbisComment_Entry *comments =
block->data.vorbis_comment.comments;
pcm_buffer_deinit(&data->buffer);
for (unsigned i = block->data.vorbis_comment.num_comments; i > 0; --i)
flac_parse_comment(tag, char_tnum, comments++);
if (data->tag != NULL)
tag_free(data->tag);
}
static enum sample_format
flac_sample_format(unsigned bits_per_sample)
{
switch (bits_per_sample) {
case 8:
return SAMPLE_FORMAT_S8;
case 16:
return SAMPLE_FORMAT_S16;
case 24:
return SAMPLE_FORMAT_S24_P32;
case 32:
return SAMPLE_FORMAT_S32;
default:
return SAMPLE_FORMAT_UNDEFINED;
}
}
static void
flac_got_stream_info(struct flac_data *data,
const FLAC__StreamMetadata_StreamInfo *stream_info)
{
if (data->initialized || data->unsupported)
return;
GError *error = NULL;
if (!audio_format_init_checked(&data->audio_format,
stream_info->sample_rate,
flac_sample_format(stream_info->bits_per_sample),
stream_info->channels, &error)) {
g_warning("%s", error->message);
g_error_free(error);
data->unsupported = true;
return;
}
data->frame_size = audio_format_frame_size(&data->audio_format);
if (data->total_frames == 0)
data->total_frames = stream_info->total_samples;
data->initialized = true;
}
void flac_metadata_common_cb(const FLAC__StreamMetadata * block,
struct flac_data *data)
{
const FLAC__StreamMetadata_StreamInfo *si = &(block->data.stream_info);
if (data->unsupported)
return;
struct replay_gain_info rgi;
char *mixramp_start;
char *mixramp_end;
float replay_gain_db = 0;
switch (block->type) {
case FLAC__METADATA_TYPE_STREAMINFO:
data->audio_format.bits = (int8_t)si->bits_per_sample;
data->audio_format.sample_rate = si->sample_rate;
data->audio_format.channels = (int8_t)si->channels;
data->total_time = ((float)si->total_samples) / (si->sample_rate);
flac_got_stream_info(data, &block->data.stream_info);
break;
case FLAC__METADATA_TYPE_VORBIS_COMMENT:
flac_parse_replay_gain(block, data);
if (flac_parse_replay_gain(&rgi, block))
replay_gain_db = decoder_replay_gain(data->decoder, &rgi);
if (flac_parse_mixramp(&mixramp_start, &mixramp_end, block)) {
g_debug("setting mixramp_tags");
decoder_mixramp(data->decoder, replay_gain_db,
mixramp_start, mixramp_end);
}
if (data->tag != NULL)
flac_vorbis_comments_to_tag(data->tag, NULL, block);
flac_vorbis_comments_to_tag(data->tag, NULL,
&block->data.vorbis_comment);
default:
break;
@@ -239,187 +161,82 @@ void flac_error_common_cb(const char *plugin,
}
}
static void flac_convert_stereo16(int16_t *dest,
const FLAC__int32 * const buf[],
unsigned int position, unsigned int end)
{
for (; position < end; ++position) {
*dest++ = buf[0][position];
*dest++ = buf[1][position];
}
}
static void
flac_convert_16(int16_t *dest,
unsigned int num_channels,
const FLAC__int32 * const buf[],
unsigned int position, unsigned int end)
{
unsigned int c_chan;
for (; position < end; ++position)
for (c_chan = 0; c_chan < num_channels; c_chan++)
*dest++ = buf[c_chan][position];
}
/**
* Note: this function also handles 24 bit files!
* This function attempts to call decoder_initialized() in case there
* was no STREAMINFO block. This is allowed for nonseekable streams,
* where the server sends us only a part of the file, without
* providing the STREAMINFO block from the beginning of the file
* (e.g. when seeking with SqueezeBox Server).
*/
static void
flac_convert_32(int32_t *dest,
unsigned int num_channels,
const FLAC__int32 * const buf[],
unsigned int position, unsigned int end)
static bool
flac_got_first_frame(struct flac_data *data, const FLAC__FrameHeader *header)
{
unsigned int c_chan;
if (data->unsupported)
return false;
for (; position < end; ++position)
for (c_chan = 0; c_chan < num_channels; c_chan++)
*dest++ = buf[c_chan][position];
}
static void
flac_convert_8(int8_t *dest,
unsigned int num_channels,
const FLAC__int32 * const buf[],
unsigned int position, unsigned int end)
{
unsigned int c_chan;
for (; position < end; ++position)
for (c_chan = 0; c_chan < num_channels; c_chan++)
*dest++ = buf[c_chan][position];
}
static void flac_convert(unsigned char *dest,
unsigned int num_channels,
unsigned int bytes_per_sample,
const FLAC__int32 * const buf[],
unsigned int position, unsigned int end)
{
switch (bytes_per_sample) {
case 2:
if (num_channels == 2)
flac_convert_stereo16((int16_t*)dest, buf,
position, end);
else
flac_convert_16((int16_t*)dest, num_channels, buf,
position, end);
break;
case 4:
flac_convert_32((int32_t*)dest, num_channels, buf,
position, end);
break;
case 1:
flac_convert_8((int8_t*)dest, num_channels, buf,
position, end);
break;
GError *error = NULL;
if (!audio_format_init_checked(&data->audio_format,
header->sample_rate,
flac_sample_format(header->bits_per_sample),
header->channels, &error)) {
g_warning("%s", error->message);
g_error_free(error);
data->unsupported = true;
return false;
}
data->frame_size = audio_format_frame_size(&data->audio_format);
decoder_initialized(data->decoder, &data->audio_format,
data->input_stream->seekable,
(float)data->total_frames /
(float)data->audio_format.sample_rate);
data->initialized = true;
return true;
}
FLAC__StreamDecoderWriteStatus
flac_common_write(struct flac_data *data, const FLAC__Frame * frame,
const FLAC__int32 *const buf[])
const FLAC__int32 *const buf[],
FLAC__uint64 nbytes)
{
unsigned int c_samp;
const unsigned int num_channels = frame->header.channels;
const unsigned int bytes_per_sample =
audio_format_sample_size(&data->audio_format);
const unsigned int bytes_per_channel =
bytes_per_sample * frame->header.channels;
const unsigned int max_samples = FLAC_CHUNK_SIZE / bytes_per_channel;
unsigned int num_samples;
enum decoder_command cmd;
void *buffer;
unsigned bit_rate;
if (bytes_per_sample != 1 && bytes_per_sample != 2 &&
bytes_per_sample != 4)
/* exotic unsupported bit rate */
if (!data->initialized && !flac_got_first_frame(data, &frame->header))
return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
for (c_samp = 0; c_samp < frame->header.blocksize;
c_samp += num_samples) {
num_samples = frame->header.blocksize - c_samp;
if (num_samples > max_samples)
num_samples = max_samples;
size_t buffer_size = frame->header.blocksize * data->frame_size;
buffer = pcm_buffer_get(&data->buffer, buffer_size);
flac_convert(data->chunk,
num_channels, bytes_per_sample, buf,
c_samp, c_samp + num_samples);
flac_convert(buffer, frame->header.channels,
data->audio_format.format, buf,
0, frame->header.blocksize);
if (nbytes > 0)
bit_rate = nbytes * 8 * frame->header.sample_rate /
(1000 * frame->header.blocksize);
else
bit_rate = 0;
cmd = decoder_data(data->decoder, data->input_stream,
data->chunk,
num_samples * bytes_per_channel,
data->time, data->bit_rate,
data->replay_gain_info);
buffer, buffer_size,
bit_rate);
data->next_frame += frame->header.blocksize;
switch (cmd) {
case DECODE_COMMAND_NONE:
case DECODE_COMMAND_START:
break;
case DECODE_COMMAND_STOP:
return
FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
case DECODE_COMMAND_SEEK:
return
FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
}
return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
}
return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
}
#if defined(FLAC_API_VERSION_CURRENT) && FLAC_API_VERSION_CURRENT > 7
char*
flac_cue_track( const char* pathname,
const unsigned int tnum)
{
FLAC__bool success;
FLAC__StreamMetadata* cs;
success = FLAC__metadata_get_cuesheet(pathname, &cs);
if (!success)
return NULL;
assert(cs != NULL);
if (cs->data.cue_sheet.num_tracks <= 1)
{
FLAC__metadata_object_delete(cs);
return NULL;
}
if (tnum > 0 && tnum < cs->data.cue_sheet.num_tracks)
{
char* track = g_strdup_printf("track_%03u.flac", tnum);
FLAC__metadata_object_delete(cs);
return track;
}
else
{
FLAC__metadata_object_delete(cs);
return NULL;
}
}
unsigned int
flac_vtrack_tnum(const char* fname)
{
/* find last occurrence of '_' in fname
* which is hopefully something like track_xxx.flac
* another/better way would be to use tag struct
*/
char* ptr = strrchr(fname, '_');
if (ptr == NULL)
return 0;
// copy ascii tracknumber to int
return (unsigned int)strtol(++ptr, NULL, 10);
}
#endif /* FLAC_API_VERSION_CURRENT >= 7 */

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2003-2009 The Music Player Daemon Project
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -24,136 +24,62 @@
#ifndef MPD_FLAC_COMMON_H
#define MPD_FLAC_COMMON_H
#include "../decoder_api.h"
#include "config.h"
#include "decoder_api.h"
#include "pcm_buffer.h"
#include <glib.h>
#include <FLAC/stream_decoder.h>
#include <FLAC/metadata.h>
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "flac"
#include <FLAC/export.h>
#if !defined(FLAC_API_VERSION_CURRENT) || FLAC_API_VERSION_CURRENT <= 7
# include <FLAC/seekable_stream_decoder.h>
# define flac_decoder FLAC__SeekableStreamDecoder
# define flac_new() FLAC__seekable_stream_decoder_new()
# define flac_ogg_init(a,b,c,d,e,f,g,h,i,j) (0)
# define flac_get_decode_position(x,y) \
FLAC__seekable_stream_decoder_get_decode_position(x,y)
# define flac_get_state(x) FLAC__seekable_stream_decoder_get_state(x)
# define flac_process_single(x) FLAC__seekable_stream_decoder_process_single(x)
# define flac_process_metadata(x) \
FLAC__seekable_stream_decoder_process_until_end_of_metadata(x)
# define flac_seek_absolute(x,y) \
FLAC__seekable_stream_decoder_seek_absolute(x,y)
# define flac_finish(x) FLAC__seekable_stream_decoder_finish(x)
# define flac_delete(x) FLAC__seekable_stream_decoder_delete(x)
# define flac_decoder_eof FLAC__SEEKABLE_STREAM_DECODER_END_OF_STREAM
typedef unsigned flac_read_status_size_t;
# define flac_read_status FLAC__SeekableStreamDecoderReadStatus
# define flac_read_status_continue \
FLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_OK
# define flac_read_status_eof FLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_OK
# define flac_read_status_abort \
FLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_ERROR
# define flac_seek_status FLAC__SeekableStreamDecoderSeekStatus
# define flac_seek_status_ok FLAC__SEEKABLE_STREAM_DECODER_SEEK_STATUS_OK
# define flac_seek_status_error FLAC__SEEKABLE_STREAM_DECODER_SEEK_STATUS_ERROR
# define flac_tell_status FLAC__SeekableStreamDecoderTellStatus
# define flac_tell_status_ok FLAC__SEEKABLE_STREAM_DECODER_TELL_STATUS_OK
# define flac_tell_status_error \
FLAC__SEEKABLE_STREAM_DECODER_TELL_STATUS_ERROR
# define flac_tell_status_unsupported \
FLAC__SEEKABLE_STREAM_DECODER_TELL_STATUS_ERROR
# define flac_length_status FLAC__SeekableStreamDecoderLengthStatus
# define flac_length_status_ok FLAC__SEEKABLE_STREAM_DECODER_LENGTH_STATUS_OK
# define flac_length_status_error \
FLAC__SEEKABLE_STREAM_DECODER_LENGTH_STATUS_ERROR
# define flac_length_status_unsupported \
FLAC__SEEKABLE_STREAM_DECODER_LENGTH_STATUS_ERROR
# ifdef HAVE_OGGFLAC
# include <OggFLAC/seekable_stream_decoder.h>
# endif
#else /* FLAC_API_VERSION_CURRENT > 7 */
/*
* OggFLAC support is handled by our flac_plugin already, and
* thus we *can* always have it if libFLAC was compiled with it
*/
# include "_ogg_common.h"
# include <FLAC/stream_decoder.h>
# define flac_decoder FLAC__StreamDecoder
# define flac_new() FLAC__stream_decoder_new()
# define flac_init(a,b,c,d,e,f,g,h,i,j) \
(FLAC__stream_decoder_init_stream(a,b,c,d,e,f,g,h,i,j) \
== FLAC__STREAM_DECODER_INIT_STATUS_OK)
# define flac_ogg_init(a,b,c,d,e,f,g,h,i,j) \
(FLAC__stream_decoder_init_ogg_stream(a,b,c,d,e,f,g,h,i,j) \
== FLAC__STREAM_DECODER_INIT_STATUS_OK)
# define flac_get_decode_position(x,y) \
FLAC__stream_decoder_get_decode_position(x,y)
# define flac_get_state(x) FLAC__stream_decoder_get_state(x)
# define flac_process_single(x) FLAC__stream_decoder_process_single(x)
# define flac_process_metadata(x) \
FLAC__stream_decoder_process_until_end_of_metadata(x)
# define flac_seek_absolute(x,y) FLAC__stream_decoder_seek_absolute(x,y)
# define flac_finish(x) FLAC__stream_decoder_finish(x)
# define flac_delete(x) FLAC__stream_decoder_delete(x)
# define flac_decoder_eof FLAC__STREAM_DECODER_END_OF_STREAM
typedef size_t flac_read_status_size_t;
# define flac_read_status FLAC__StreamDecoderReadStatus
# define flac_read_status_continue \
FLAC__STREAM_DECODER_READ_STATUS_CONTINUE
# define flac_read_status_eof FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM
# define flac_read_status_abort FLAC__STREAM_DECODER_READ_STATUS_ABORT
# define flac_seek_status FLAC__StreamDecoderSeekStatus
# define flac_seek_status_ok FLAC__STREAM_DECODER_SEEK_STATUS_OK
# define flac_seek_status_error FLAC__STREAM_DECODER_SEEK_STATUS_ERROR
# define flac_seek_status_unsupported \
FLAC__STREAM_DECODER_SEEK_STATUS_UNSUPPORTED
# define flac_tell_status FLAC__StreamDecoderTellStatus
# define flac_tell_status_ok FLAC__STREAM_DECODER_TELL_STATUS_OK
# define flac_tell_status_error FLAC__STREAM_DECODER_TELL_STATUS_ERROR
# define flac_tell_status_unsupported \
FLAC__STREAM_DECODER_TELL_STATUS_UNSUPPORTED
# define flac_length_status FLAC__StreamDecoderLengthStatus
# define flac_length_status_ok FLAC__STREAM_DECODER_LENGTH_STATUS_OK
# define flac_length_status_error FLAC__STREAM_DECODER_LENGTH_STATUS_ERROR
# define flac_length_status_unsupported \
FLAC__STREAM_DECODER_LENGTH_STATUS_UNSUPPORTED
#endif /* FLAC_API_VERSION_CURRENT >= 7 */
#include <FLAC/metadata.h>
#define FLAC_CHUNK_SIZE 4080
struct flac_data {
unsigned char chunk[FLAC_CHUNK_SIZE];
float time;
unsigned int bit_rate;
struct pcm_buffer buffer;
/**
* The size of one frame in the output buffer.
*/
unsigned frame_size;
/**
* Has decoder_initialized() been called yet?
*/
bool initialized;
/**
* Does the FLAC file contain an unsupported audio format?
*/
bool unsupported;
/**
* The validated audio format of the FLAC file. This
* attribute is defined if "initialized" is true.
*/
struct audio_format audio_format;
float total_time;
/**
* The total number of frames in this song. The decoder
* plugin may initialize this attribute to override the value
* provided by libFLAC (e.g. for sub songs from a CUE sheet).
*/
FLAC__uint64 total_frames;
/**
* The number of the first frame in this song. This is only
* non-zero if playing sub songs from a CUE sheet.
*/
FLAC__uint64 first_frame;
/**
* The number of the next frame which is going to be decoded.
*/
FLAC__uint64 next_frame;
FLAC__uint64 position;
struct decoder *decoder;
struct input_stream *input_stream;
struct replay_gain_info *replay_gain_info;
struct tag *tag;
};
@@ -162,6 +88,9 @@ void
flac_data_init(struct flac_data *data, struct decoder * decoder,
struct input_stream *input_stream);
void
flac_data_deinit(struct flac_data *data);
void flac_metadata_common_cb(const FLAC__StreamMetadata * block,
struct flac_data *data);
@@ -169,23 +98,9 @@ void flac_error_common_cb(const char *plugin,
FLAC__StreamDecoderErrorStatus status,
struct flac_data *data);
void
flac_vorbis_comments_to_tag(struct tag *tag, const char *char_tnum,
const FLAC__StreamMetadata *block);
FLAC__StreamDecoderWriteStatus
flac_common_write(struct flac_data *data, const FLAC__Frame * frame,
const FLAC__int32 *const buf[]);
#if defined(FLAC_API_VERSION_CURRENT) && FLAC_API_VERSION_CURRENT > 7
char*
flac_cue_track( const char* pathname,
const unsigned int tnum);
unsigned int
flac_vtrack_tnum( const char*);
#endif /* FLAC_API_VERSION_CURRENT >= 7 */
const FLAC__int32 *const buf[],
FLAC__uint64 nbytes);
#endif /* _FLAC_COMMON_H */

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2003-2009 The Music Player Daemon Project
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -21,8 +21,8 @@
* Common functions used for Ogg data streams (Ogg-Vorbis and OggFLAC)
*/
#include "config.h"
#include "_ogg_common.h"
#include "../utils.h"
ogg_stream_type ogg_stream_type_detect(struct input_stream *inStream)
{

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2003-2009 The Music Player Daemon Project
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -24,7 +24,7 @@
#ifndef MPD_OGG_COMMON_H
#define MPD_OGG_COMMON_H
#include "../decoder_api.h"
#include "decoder_api.h"
typedef enum _ogg_stream_type { VORBIS, FLAC } ogg_stream_type;

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2003-2009 The Music Player Daemon Project
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,7 +17,9 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "../decoder_api.h"
#include "config.h"
#include "decoder_api.h"
#include "audio_check.h"
#include <audiofile.h>
#include <af_vfs.h>
@@ -45,10 +47,20 @@ static int audiofile_get_duration(const char *file)
}
static ssize_t
audiofile_file_read(AFvirtualfile *vfile, void *data, size_t nbytes)
audiofile_file_read(AFvirtualfile *vfile, void *data, size_t length)
{
struct input_stream *is = (struct input_stream *) vfile->closure;
return input_stream_read(is, data, nbytes);
GError *error = NULL;
size_t nbytes;
nbytes = input_stream_read(is, data, length, &error);
if (nbytes == 0 && error != NULL) {
g_warning("%s", error->message);
g_error_free(error);
return -1;
}
return nbytes;
}
static long
@@ -78,7 +90,7 @@ audiofile_file_seek(AFvirtualfile *vfile, long offset, int is_relative)
{
struct input_stream *is = (struct input_stream *) vfile->closure;
int whence = (is_relative ? SEEK_CUR : SEEK_SET);
if (input_stream_seek(is, offset, whence)) {
if (input_stream_seek(is, offset, whence, NULL)) {
return is->offset;
} else {
return -1;
@@ -99,17 +111,56 @@ setup_virtual_fops(struct input_stream *stream)
return vf;
}
static enum sample_format
audiofile_bits_to_sample_format(int bits)
{
switch (bits) {
case 8:
return SAMPLE_FORMAT_S8;
case 16:
return SAMPLE_FORMAT_S16;
case 24:
return SAMPLE_FORMAT_S24_P32;
case 32:
return SAMPLE_FORMAT_S32;
}
return SAMPLE_FORMAT_UNDEFINED;
}
static enum sample_format
audiofile_setup_sample_format(AFfilehandle af_fp)
{
int fs, bits;
afGetSampleFormat(af_fp, AF_DEFAULT_TRACK, &fs, &bits);
if (!audio_valid_sample_format(audiofile_bits_to_sample_format(bits))) {
g_debug("input file has %d bit samples, converting to 16",
bits);
bits = 16;
}
afSetVirtualSampleFormat(af_fp, AF_DEFAULT_TRACK,
AF_SAMPFMT_TWOSCOMP, bits);
afGetVirtualSampleFormat(af_fp, AF_DEFAULT_TRACK, &fs, &bits);
return audiofile_bits_to_sample_format(bits);
}
static void
audiofile_stream_decode(struct decoder *decoder, struct input_stream *is)
{
GError *error = NULL;
AFvirtualfile *vf;
int fs, frame_count;
AFfilehandle af_fp;
int bits;
struct audio_format audio_format;
float total_time;
uint16_t bit_rate;
int ret, current = 0;
int ret;
char chunk[CHUNK_SIZE];
enum decoder_command cmd;
@@ -126,26 +177,13 @@ audiofile_stream_decode(struct decoder *decoder, struct input_stream *is)
return;
}
afGetSampleFormat(af_fp, AF_DEFAULT_TRACK, &fs, &bits);
if (!audio_valid_sample_format(bits)) {
g_debug("input file has %d bit samples, converting to 16",
bits);
bits = 16;
}
afSetVirtualSampleFormat(af_fp, AF_DEFAULT_TRACK,
AF_SAMPFMT_TWOSCOMP, bits);
afGetVirtualSampleFormat(af_fp, AF_DEFAULT_TRACK, &fs, &bits);
audio_format.bits = (uint8_t)bits;
audio_format.sample_rate =
(unsigned int)afGetRate(af_fp, AF_DEFAULT_TRACK);
audio_format.channels =
(uint8_t)afGetVirtualChannels(af_fp, AF_DEFAULT_TRACK);
if (!audio_format_valid(&audio_format)) {
g_warning("Invalid audio format: %u:%u:%u\n",
audio_format.sample_rate, audio_format.bits,
audio_format.channels);
if (!audio_format_init_checked(&audio_format,
afGetRate(af_fp, AF_DEFAULT_TRACK),
audiofile_setup_sample_format(af_fp),
afGetVirtualChannels(af_fp, AF_DEFAULT_TRACK),
&error)) {
g_warning("%s", error->message);
g_error_free(error);
afCloseFile(af_fp);
return;
}
@@ -166,17 +204,14 @@ audiofile_stream_decode(struct decoder *decoder, struct input_stream *is)
if (ret <= 0)
break;
current += ret;
cmd = decoder_data(decoder, NULL,
chunk, ret * fs,
(float)current /
(float)audio_format.sample_rate,
bit_rate, NULL);
bit_rate);
if (cmd == DECODE_COMMAND_SEEK) {
current = decoder_seek_where(decoder) *
AFframecount frame = decoder_seek_where(decoder) *
audio_format.sample_rate;
afSeekFrame(af_fp, AF_DEFAULT_TRACK, current);
afSeekFrame(af_fp, AF_DEFAULT_TRACK, frame);
decoder_command_finished(decoder);
cmd = DECODE_COMMAND_NONE;

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2003-2009 The Music Player Daemon Project
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,9 +17,10 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "../decoder_api.h"
#include "decoder_buffer.h"
#include "config.h"
#include "decoder_api.h"
#include "decoder_buffer.h"
#include "audio_check.h"
#define AAC_MAX_CHANNELS 6
@@ -36,6 +37,15 @@ static const unsigned adts_sample_rates[] =
16000, 12000, 11025, 8000, 7350, 0, 0, 0
};
/**
* The GLib quark used for errors reported by this plugin.
*/
static inline GQuark
faad_decoder_quark(void)
{
return g_quark_from_static_string("faad");
}
/**
* Check whether the buffer head is an AAC frame, and return the frame
* length. Returns 0 if it is not a frame.
@@ -195,7 +205,7 @@ faad_song_duration(struct decoder_buffer *buffer, struct input_stream *is)
/* obtain the duration from the ADTS header */
float song_length = adts_song_duration(buffer);
input_stream_seek(is, tagsize, SEEK_SET);
input_stream_seek(is, tagsize, SEEK_SET, NULL);
data = decoder_buffer_read(buffer, &length);
if (data != NULL)
@@ -232,7 +242,7 @@ faad_song_duration(struct decoder_buffer *buffer, struct input_stream *is)
*/
static bool
faad_decoder_init(faacDecHandle decoder, struct decoder_buffer *buffer,
struct audio_format *audio_format)
struct audio_format *audio_format, GError **error_r)
{
union {
/* deconst hack for libfaad */
@@ -247,32 +257,33 @@ faad_decoder_init(faacDecHandle decoder, struct decoder_buffer *buffer,
/* neaacdec.h declares all arguments as "unsigned long", but
internally expects uint32_t pointers. To avoid gcc
warnings, use this workaround. */
unsigned long *sample_rate_r = (unsigned long *)(void *)&sample_rate;
unsigned long *sample_rate_p = (unsigned long *)(void *)&sample_rate;
#else
uint32_t *sample_rate_r = &sample_rate;
uint32_t *sample_rate_p = &sample_rate;
#endif
u.in = decoder_buffer_read(buffer, &length);
if (u.in == NULL)
if (u.in == NULL) {
g_set_error(error_r, faad_decoder_quark(), 0,
"Empty file");
return false;
}
nbytes = faacDecInit(decoder, u.out,
#ifdef HAVE_FAAD_BUFLEN_FUNCS
length,
#endif
sample_rate_r, &channels);
if (nbytes < 0)
sample_rate_p, &channels);
if (nbytes < 0) {
g_set_error(error_r, faad_decoder_quark(), 0,
"Not an AAC stream");
return false;
}
decoder_buffer_consume(buffer, nbytes);
*audio_format = (struct audio_format){
.bits = 16,
.channels = channels,
.sample_rate = sample_rate,
};
return true;
return audio_format_init_checked(audio_format, sample_rate,
SAMPLE_FORMAT_S16, channels, error_r);
}
/**
@@ -311,20 +322,16 @@ faad_decoder_decode(faacDecHandle decoder, struct decoder_buffer *buffer,
* file is invalid.
*/
static float
faad_get_file_time_float(const char *file)
faad_get_file_time_float(struct input_stream *is)
{
struct decoder_buffer *buffer;
float length;
faacDecHandle decoder;
faacDecConfigurationPtr config;
struct input_stream is;
if (!input_stream_open(&is, file))
return -1;
buffer = decoder_buffer_new(NULL, &is,
buffer = decoder_buffer_new(NULL, is,
FAAD_MIN_STREAMSIZE * AAC_MAX_CHANNELS);
length = faad_song_duration(buffer, &is);
length = faad_song_duration(buffer, is);
if (length < 0) {
bool ret;
@@ -338,15 +345,14 @@ faad_get_file_time_float(const char *file)
decoder_buffer_fill(buffer);
ret = faad_decoder_init(decoder, buffer, &audio_format);
if (ret && audio_format_valid(&audio_format))
ret = faad_decoder_init(decoder, buffer, &audio_format, NULL);
if (ret)
length = 0;
faacDecClose(decoder);
}
decoder_buffer_free(buffer);
input_stream_close(&is);
return length;
}
@@ -357,12 +363,12 @@ faad_get_file_time_float(const char *file)
* file is invalid.
*/
static int
faad_get_file_time(const char *file)
faad_get_file_time(struct input_stream *is)
{
int file_time = -1;
float length;
if ((length = faad_get_file_time_float(file)) >= 0)
if ((length = faad_get_file_time_float(is)) >= 0)
file_time = length + 0.5;
return file_time;
@@ -371,7 +377,7 @@ faad_get_file_time(const char *file)
static void
faad_stream_decode(struct decoder *mpd_decoder, struct input_stream *is)
{
float file_time;
GError *error = NULL;
float total_time = 0;
faacDecHandle decoder;
struct audio_format audio_format;
@@ -408,15 +414,10 @@ faad_stream_decode(struct decoder *mpd_decoder, struct input_stream *is)
/* initialize it */
ret = faad_decoder_init(decoder, buffer, &audio_format);
ret = faad_decoder_init(decoder, buffer, &audio_format, &error);
if (!ret) {
g_warning("Error not a AAC stream.\n");
faacDecClose(decoder);
return;
}
if (!audio_format_valid(&audio_format)) {
g_warning("invalid audio format\n");
g_warning("%s", error->message);
g_error_free(error);
faacDecClose(decoder);
return;
}
@@ -427,8 +428,6 @@ faad_stream_decode(struct decoder *mpd_decoder, struct input_stream *is)
/* the decoder loop */
file_time = 0.0;
do {
size_t frame_size;
const void *decoded;
@@ -474,16 +473,13 @@ faad_stream_decode(struct decoder *mpd_decoder, struct input_stream *is)
bit_rate = frame_info.bytesconsumed * 8.0 *
frame_info.channels * audio_format.sample_rate /
frame_info.samples / 1000 + 0.5;
file_time +=
(float)(frame_info.samples) / frame_info.channels /
audio_format.sample_rate;
}
/* send PCM samples to MPD */
cmd = decoder_data(mpd_decoder, is, decoded,
(size_t)frame_info.samples * 2, file_time,
bit_rate, NULL);
(size_t)frame_info.samples * 2,
bit_rate);
} while (cmd != DECODE_COMMAND_STOP);
/* cleanup */
@@ -492,15 +488,13 @@ faad_stream_decode(struct decoder *mpd_decoder, struct input_stream *is)
}
static struct tag *
faad_tag_dup(const char *file)
faad_stream_tag(struct input_stream *is)
{
int file_time = faad_get_file_time(file);
int file_time = faad_get_file_time(is);
struct tag *tag;
if (file_time < 0) {
g_debug("Failed to get total song time from: %s", file);
if (file_time < 0)
return NULL;
}
tag = tag_new();
tag->time = file_time;
@@ -515,7 +509,7 @@ static const char *const faad_mime_types[] = {
const struct decoder_plugin faad_decoder_plugin = {
.name = "faad",
.stream_decode = faad_stream_decode,
.tag_dup = faad_tag_dup,
.stream_tag = faad_stream_tag,
.suffixes = faad_suffixes,
.mime_types = faad_mime_types,
};

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2003-2009 The Music Player Daemon Project
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,8 +17,9 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "../decoder_api.h"
#include "config.h"
#include "decoder_api.h"
#include "audio_check.h"
#include <glib.h>
@@ -39,93 +40,114 @@
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavformat/avio.h>
#include <libavutil/log.h>
#endif
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "ffmpeg"
struct ffmpeg_context {
int audio_stream;
AVFormatContext *format_context;
AVCodecContext *codec_context;
struct decoder *decoder;
struct input_stream *input;
struct tag *tag;
};
#ifndef OLD_FFMPEG_INCLUDES
struct ffmpeg_stream {
/** hack - see url_to_struct() */
char url[64];
struct decoder *decoder;
struct input_stream *input;
};
/**
* Convert a faked mpd:// URL to a ffmpeg_stream structure. This is a
* hack because ffmpeg does not provide a nice API for passing a
* user-defined pointer to mpdurl_open().
*/
static struct ffmpeg_stream *url_to_struct(const char *url)
static GLogLevelFlags
level_ffmpeg_to_glib(int level)
{
union {
const char *in;
struct ffmpeg_stream *out;
} u = { .in = url };
return u.out;
if (level <= AV_LOG_FATAL)
return G_LOG_LEVEL_CRITICAL;
if (level <= AV_LOG_ERROR)
return G_LOG_LEVEL_WARNING;
if (level <= AV_LOG_INFO)
return G_LOG_LEVEL_MESSAGE;
return G_LOG_LEVEL_DEBUG;
}
static int mpd_ffmpeg_open(URLContext *h, const char *filename,
G_GNUC_UNUSED int flags)
static void
mpd_ffmpeg_log_callback(G_GNUC_UNUSED void *ptr, int level,
const char *fmt, va_list vl)
{
struct ffmpeg_stream *stream = url_to_struct(filename);
h->priv_data = stream;
h->is_streamed = stream->input->seekable ? 0 : 1;
return 0;
const AVClass * cls = NULL;
if (ptr != NULL)
cls = *(const AVClass *const*)ptr;
if (cls != NULL) {
char *domain = g_strconcat(G_LOG_DOMAIN, "/", cls->item_name(ptr), NULL);
g_logv(domain, level_ffmpeg_to_glib(level), fmt, vl);
g_free(domain);
}
}
static int mpd_ffmpeg_read(URLContext *h, unsigned char *buf, int size)
#endif /* !OLD_FFMPEG_INCLUDES */
struct mpd_ffmpeg_stream {
struct decoder *decoder;
struct input_stream *input;
ByteIOContext *io;
unsigned char buffer[8192];
};
static int
mpd_ffmpeg_stream_read(void *opaque, uint8_t *buf, int size)
{
struct ffmpeg_stream *stream = (struct ffmpeg_stream *) h->priv_data;
struct mpd_ffmpeg_stream *stream = opaque;
return decoder_read(stream->decoder, stream->input,
(void *)buf, size);
}
static int64_t mpd_ffmpeg_seek(URLContext *h, int64_t pos, int whence)
static int64_t
mpd_ffmpeg_stream_seek(void *opaque, int64_t pos, int whence)
{
struct ffmpeg_stream *stream = (struct ffmpeg_stream *) h->priv_data;
struct mpd_ffmpeg_stream *stream = opaque;
bool ret;
if (whence == AVSEEK_SIZE)
return stream->input->size;
ret = input_stream_seek(stream->input, pos, whence);
ret = input_stream_seek(stream->input, pos, whence, NULL);
if (!ret)
return -1;
return stream->input->offset;
}
static int mpd_ffmpeg_close(URLContext *h)
static struct mpd_ffmpeg_stream *
mpd_ffmpeg_stream_open(struct decoder *decoder, struct input_stream *input)
{
h->priv_data = NULL;
return 0;
struct mpd_ffmpeg_stream *stream = g_new(struct mpd_ffmpeg_stream, 1);
stream->decoder = decoder;
stream->input = input;
stream->io = av_alloc_put_byte(stream->buffer, sizeof(stream->buffer),
false, stream,
mpd_ffmpeg_stream_read, NULL,
input->seekable
? mpd_ffmpeg_stream_seek : NULL);
if (stream->io == NULL) {
g_free(stream);
return NULL;
}
return stream;
}
static URLProtocol mpd_ffmpeg_fileops = {
.name = "mpd",
.url_open = mpd_ffmpeg_open,
.url_read = mpd_ffmpeg_read,
.url_seek = mpd_ffmpeg_seek,
.url_close = mpd_ffmpeg_close,
};
static void
mpd_ffmpeg_stream_close(struct mpd_ffmpeg_stream *stream)
{
av_free(stream->io);
g_free(stream);
}
static bool
ffmpeg_init(G_GNUC_UNUSED const struct config_param *param)
{
#ifndef OLD_FFMPEG_INCLUDES
av_log_set_callback(mpd_ffmpeg_log_callback);
#endif
av_register_all();
register_protocol(&mpd_ffmpeg_fileops);
return true;
}
@@ -140,98 +162,6 @@ ffmpeg_find_audio_stream(const AVFormatContext *format_context)
return -1;
}
/**
* Append the suffix of the original URI to the virtual stream URI.
* Without this, libavformat cannot detect some of the codecs
* (e.g. "shorten").
*/
static void
append_uri_suffix(struct ffmpeg_stream *stream, const char *uri)
{
assert(stream != NULL);
assert(uri != NULL);
char *base = g_path_get_basename(uri);
const char *suffix = strrchr(base, '.');
if (suffix != NULL && suffix[1] != 0)
g_strlcat(stream->url, suffix, sizeof(stream->url));
g_free(base);
}
static bool
ffmpeg_helper(const char *uri, struct input_stream *input,
bool (*callback)(struct ffmpeg_context *ctx),
struct ffmpeg_context *ctx)
{
AVFormatContext *format_context;
AVCodecContext *codec_context;
AVCodec *codec;
int audio_stream;
struct ffmpeg_stream stream = {
.url = "mpd://X", /* only the mpd:// prefix matters */
};
bool ret;
if (uri != NULL)
append_uri_suffix(&stream, uri);
stream.input = input;
if (ctx && ctx->decoder) {
stream.decoder = ctx->decoder; //are we in decoding loop ?
} else {
stream.decoder = NULL;
}
//ffmpeg works with ours "fileops" helper
if (av_open_input_file(&format_context, stream.url, NULL, 0, NULL) != 0) {
g_warning("Open failed\n");
return false;
}
if (av_find_stream_info(format_context)<0) {
g_warning("Couldn't find stream info\n");
return false;
}
audio_stream = ffmpeg_find_audio_stream(format_context);
if (audio_stream == -1) {
g_warning("No audio stream inside\n");
return false;
}
codec_context = format_context->streams[audio_stream]->codec;
if (codec_context->codec_name[0] != 0)
g_debug("codec '%s'", codec_context->codec_name);
codec = avcodec_find_decoder(codec_context->codec_id);
if (!codec) {
g_warning("Unsupported audio codec\n");
return false;
}
if (avcodec_open(codec_context, codec)<0) {
g_warning("Could not open codec\n");
return false;
}
if (callback) {
ctx->audio_stream = audio_stream;
ctx->format_context = format_context;
ctx->codec_context = codec_context;
ret = callback(ctx);
} else
ret = true;
avcodec_close(codec_context);
av_close_input_file(format_context);
return ret;
}
/**
* On some platforms, libavcodec wants the output buffer aligned to 16
* bytes (because it uses SSE/Altivec internally). This function
@@ -254,7 +184,6 @@ ffmpeg_send_packet(struct decoder *decoder, struct input_stream *is,
const AVRational *time_base)
{
enum decoder_command cmd = DECODE_COMMAND_NONE;
int position;
uint8_t audio_buf[(AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 2 + 16];
int16_t *aligned_buffer;
size_t buffer_size;
@@ -262,6 +191,11 @@ ffmpeg_send_packet(struct decoder *decoder, struct input_stream *is,
uint8_t *packet_data;
int packet_size;
if (packet->pts != (int64_t)AV_NOPTS_VALUE)
decoder_timestamp(decoder,
av_rescale_q(packet->pts, *time_base,
(AVRational){1, 1}));
packet_data = packet->data;
packet_size = packet->size;
@@ -286,65 +220,165 @@ ffmpeg_send_packet(struct decoder *decoder, struct input_stream *is,
if (audio_size <= 0)
continue;
position = packet->pts != (int64_t)AV_NOPTS_VALUE
? av_rescale_q(packet->pts, *time_base,
(AVRational){1, 1})
: 0;
cmd = decoder_data(decoder, is,
aligned_buffer, audio_size,
position,
codec_context->bit_rate / 1000, NULL);
codec_context->bit_rate / 1000);
}
return cmd;
}
static bool
ffmpeg_decode_internal(struct ffmpeg_context *ctx)
static enum sample_format
ffmpeg_sample_format(G_GNUC_UNUSED const AVCodecContext *codec_context)
{
struct decoder *decoder = ctx->decoder;
AVCodecContext *codec_context = ctx->codec_context;
AVFormatContext *format_context = ctx->format_context;
AVPacket packet;
struct audio_format audio_format;
enum decoder_command cmd;
int total_time;
total_time = 0;
#if LIBAVCODEC_VERSION_INT >= ((51<<16)+(41<<8)+0)
audio_format.bits = (uint8_t) av_get_bits_per_sample_format(codec_context->sample_fmt);
switch (codec_context->sample_fmt) {
case SAMPLE_FMT_S16:
return SAMPLE_FORMAT_S16;
case SAMPLE_FMT_S32:
return SAMPLE_FORMAT_S32;
default:
g_warning("Unsupported libavcodec SampleFormat value: %d",
codec_context->sample_fmt);
return SAMPLE_FORMAT_UNDEFINED;
}
#else
/* XXX fixme 16-bit for older ffmpeg (13 Aug 2007) */
audio_format.bits = (uint8_t) 16;
return SAMPLE_FORMAT_S16;
#endif
audio_format.sample_rate = (unsigned int)codec_context->sample_rate;
audio_format.channels = codec_context->channels;
}
if (!audio_format_valid(&audio_format)) {
g_warning("Invalid audio format: %u:%u:%u\n",
audio_format.sample_rate, audio_format.bits,
audio_format.channels);
return false;
static AVInputFormat *
ffmpeg_probe(struct decoder *decoder, struct input_stream *is)
{
enum {
BUFFER_SIZE = 16384,
PADDING = 16,
};
unsigned char *buffer = g_malloc(BUFFER_SIZE);
size_t nbytes = decoder_read(decoder, is, buffer, BUFFER_SIZE);
if (nbytes <= PADDING || !input_stream_seek(is, 0, SEEK_SET, NULL)) {
g_free(buffer);
return NULL;
}
//there is some problem with this on some demux (mp3 at least)
if (format_context->duration != (int64_t)AV_NOPTS_VALUE) {
total_time = format_context->duration / AV_TIME_BASE;
/* some ffmpeg parsers (e.g. ac3_parser.c) read a few bytes
beyond the declared buffer limit, which makes valgrind
angry; this workaround removes some padding from the buffer
size */
nbytes -= PADDING;
AVProbeData avpd = {
.buf = buffer,
.buf_size = nbytes,
.filename = is->uri,
};
AVInputFormat *format = av_probe_input_format(&avpd, true);
g_free(buffer);
return format;
}
static void
ffmpeg_decode(struct decoder *decoder, struct input_stream *input)
{
AVInputFormat *input_format = ffmpeg_probe(decoder, input);
if (input_format == NULL)
return;
g_debug("detected input format '%s' (%s)",
input_format->name, input_format->long_name);
struct mpd_ffmpeg_stream *stream =
mpd_ffmpeg_stream_open(decoder, input);
if (stream == NULL) {
g_warning("Failed to open stream");
return;
}
AVFormatContext *format_context;
AVCodecContext *codec_context;
AVCodec *codec;
int audio_stream;
//ffmpeg works with ours "fileops" helper
if (av_open_input_stream(&format_context, stream->io, input->uri,
input_format, NULL) != 0) {
g_warning("Open failed\n");
mpd_ffmpeg_stream_close(stream);
return;
}
if (av_find_stream_info(format_context)<0) {
g_warning("Couldn't find stream info\n");
av_close_input_stream(format_context);
mpd_ffmpeg_stream_close(stream);
return;
}
audio_stream = ffmpeg_find_audio_stream(format_context);
if (audio_stream == -1) {
g_warning("No audio stream inside\n");
av_close_input_stream(format_context);
mpd_ffmpeg_stream_close(stream);
return;
}
codec_context = format_context->streams[audio_stream]->codec;
if (codec_context->codec_name[0] != 0)
g_debug("codec '%s'", codec_context->codec_name);
codec = avcodec_find_decoder(codec_context->codec_id);
if (!codec) {
g_warning("Unsupported audio codec\n");
av_close_input_stream(format_context);
mpd_ffmpeg_stream_close(stream);
return;
}
if (avcodec_open(codec_context, codec)<0) {
g_warning("Could not open codec\n");
av_close_input_stream(format_context);
mpd_ffmpeg_stream_close(stream);
return;
}
GError *error = NULL;
struct audio_format audio_format;
if (!audio_format_init_checked(&audio_format,
codec_context->sample_rate,
ffmpeg_sample_format(codec_context),
codec_context->channels, &error)) {
g_warning("%s", error->message);
g_error_free(error);
avcodec_close(codec_context);
av_close_input_stream(format_context);
mpd_ffmpeg_stream_close(stream);
return;
}
int total_time = format_context->duration != (int64_t)AV_NOPTS_VALUE
? format_context->duration / AV_TIME_BASE
: 0;
decoder_initialized(decoder, &audio_format,
ctx->input->seekable, total_time);
input->seekable, total_time);
enum decoder_command cmd;
do {
AVPacket packet;
if (av_read_frame(format_context, &packet) < 0)
/* end of file */
break;
if (packet.stream_index == ctx->audio_stream)
cmd = ffmpeg_send_packet(decoder, ctx->input,
if (packet.stream_index == audio_stream)
cmd = ffmpeg_send_packet(decoder, input,
&packet, codec_context,
&format_context->streams[ctx->audio_stream]->time_base);
&format_context->streams[audio_stream]->time_base);
else
cmd = decoder_get_command(decoder);
@@ -361,104 +395,121 @@ ffmpeg_decode_internal(struct ffmpeg_context *ctx)
}
} while (cmd != DECODE_COMMAND_STOP);
return true;
}
static void
ffmpeg_decode(struct decoder *decoder, struct input_stream *input)
{
struct ffmpeg_context ctx;
ctx.input = input;
ctx.decoder = decoder;
ffmpeg_helper(decoder_get_uri(decoder), input,
ffmpeg_decode_internal, &ctx);
avcodec_close(codec_context);
av_close_input_stream(format_context);
mpd_ffmpeg_stream_close(stream);
}
#if LIBAVFORMAT_VERSION_INT >= ((52<<16)+(31<<8)+0)
typedef struct ffmpeg_tag_map {
enum tag_type type;
const char *name;
} ffmpeg_tag_map;
static const ffmpeg_tag_map ffmpeg_tag_maps[] = {
{ TAG_TITLE, "title" },
#if LIBAVFORMAT_VERSION_INT >= ((52<<16)+(50<<8))
{ TAG_ARTIST, "artist" },
{ TAG_DATE, "date" },
#else
{ TAG_ARTIST, "author" },
{ TAG_DATE, "year" },
#endif
{ TAG_ALBUM, "album" },
{ TAG_COMMENT, "comment" },
{ TAG_GENRE, "genre" },
{ TAG_TRACK, "track" },
{ TAG_ARTIST_SORT, "author-sort" },
{ TAG_ALBUM_ARTIST, "album_artist" },
{ TAG_ALBUM_ARTIST_SORT, "album_artist-sort" },
{ TAG_COMPOSER, "composer" },
{ TAG_PERFORMER, "performer" },
{ TAG_DISC, "disc" },
};
static bool
ffmpeg_copy_metadata(struct tag *tag, AVMetadata *m,
enum tag_type type, const char *name)
const ffmpeg_tag_map tag_map)
{
AVMetadataTag *mt = av_metadata_get(m, name, NULL, 0);
if (mt != NULL)
tag_add_item(tag, type, mt->value);
AVMetadataTag *mt = NULL;
while ((mt = av_metadata_get(m, tag_map.name, mt, 0)) != NULL)
tag_add_item(tag, tag_map.type, mt->value);
return mt != NULL;
}
#endif
static bool ffmpeg_tag_internal(struct ffmpeg_context *ctx)
//no tag reading in ffmpeg, check if playable
static struct tag *
ffmpeg_stream_tag(struct input_stream *is)
{
struct tag *tag = (struct tag *) ctx->tag;
AVFormatContext *f = ctx->format_context;
AVInputFormat *input_format = ffmpeg_probe(NULL, is);
if (input_format == NULL)
return NULL;
tag->time = 0;
if (f->duration != (int64_t)AV_NOPTS_VALUE)
tag->time = f->duration / AV_TIME_BASE;
struct mpd_ffmpeg_stream *stream = mpd_ffmpeg_stream_open(NULL, is);
if (stream == NULL)
return NULL;
AVFormatContext *f;
if (av_open_input_stream(&f, stream->io, is->uri,
input_format, NULL) != 0) {
mpd_ffmpeg_stream_close(stream);
return NULL;
}
if (av_find_stream_info(f) < 0) {
av_close_input_stream(f);
mpd_ffmpeg_stream_close(stream);
return NULL;
}
struct tag *tag = tag_new();
tag->time = f->duration != (int64_t)AV_NOPTS_VALUE
? f->duration / AV_TIME_BASE
: 0;
#if LIBAVFORMAT_VERSION_INT >= ((52<<16)+(31<<8)+0)
av_metadata_conv(f, NULL, f->iformat->metadata_conv);
ffmpeg_copy_metadata(tag, f->metadata, TAG_ITEM_TITLE, "title");
ffmpeg_copy_metadata(tag, f->metadata, TAG_ITEM_ARTIST, "author");
ffmpeg_copy_metadata(tag, f->metadata, TAG_ITEM_ALBUM, "album");
ffmpeg_copy_metadata(tag, f->metadata, TAG_ITEM_COMMENT, "comment");
ffmpeg_copy_metadata(tag, f->metadata, TAG_ITEM_GENRE, "genre");
ffmpeg_copy_metadata(tag, f->metadata, TAG_ITEM_TRACK, "track");
ffmpeg_copy_metadata(tag, f->metadata, TAG_ITEM_DATE, "year");
for (unsigned i = 0; i < sizeof(ffmpeg_tag_maps)/sizeof(ffmpeg_tag_map); i++) {
int idx = ffmpeg_find_audio_stream(f);
ffmpeg_copy_metadata(tag, f->metadata, ffmpeg_tag_maps[i]);
if (idx >= 0)
ffmpeg_copy_metadata(tag, f->streams[idx]->metadata, ffmpeg_tag_maps[i]);
}
#else
if (f->author[0])
tag_add_item(tag, TAG_ITEM_ARTIST, f->author);
tag_add_item(tag, TAG_ARTIST, f->author);
if (f->title[0])
tag_add_item(tag, TAG_ITEM_TITLE, f->title);
tag_add_item(tag, TAG_TITLE, f->title);
if (f->album[0])
tag_add_item(tag, TAG_ITEM_ALBUM, f->album);
tag_add_item(tag, TAG_ALBUM, f->album);
if (f->track > 0) {
char buffer[16];
snprintf(buffer, sizeof(buffer), "%d", f->track);
tag_add_item(tag, TAG_ITEM_TRACK, buffer);
tag_add_item(tag, TAG_TRACK, buffer);
}
if (f->comment[0])
tag_add_item(tag, TAG_ITEM_COMMENT, f->comment);
tag_add_item(tag, TAG_COMMENT, f->comment);
if (f->genre[0])
tag_add_item(tag, TAG_ITEM_GENRE, f->genre);
tag_add_item(tag, TAG_GENRE, f->genre);
if (f->year > 0) {
char buffer[16];
snprintf(buffer, sizeof(buffer), "%d", f->year);
tag_add_item(tag, TAG_ITEM_DATE, buffer);
tag_add_item(tag, TAG_DATE, buffer);
}
#endif
return true;
}
//no tag reading in ffmpeg, check if playable
static struct tag *ffmpeg_tag(const char *file)
{
struct input_stream input;
struct ffmpeg_context ctx;
bool ret;
av_close_input_stream(f);
mpd_ffmpeg_stream_close(stream);
if (!input_stream_open(&input, file)) {
g_warning("failed to open %s\n", file);
return NULL;
}
ctx.decoder = NULL;
ctx.tag = tag_new();
ret = ffmpeg_helper(file, &input, ffmpeg_tag_internal, &ctx);
if (!ret) {
tag_free(ctx.tag);
ctx.tag = NULL;
}
input_stream_close(&input);
return ctx.tag;
return tag;
}
/**
@@ -473,7 +524,9 @@ static const char *const ffmpeg_suffixes[] = {
"atrac", "au", "aud", "avi", "avm2", "avs", "bap", "bfi", "c93", "cak",
"cin", "cmv", "cpk", "daud", "dct", "divx", "dts", "dv", "dvd", "dxa",
"eac3", "film", "flac", "flc", "fli", "fll", "flx", "flv", "g726",
"gsm", "gxf", "iss", "m1v", "m2v", "m2t", "m2ts", "m4a", "m4v", "mad",
"gsm", "gxf", "iss", "m1v", "m2v", "m2t", "m2ts",
"m4a", "m4b", "m4v",
"mad",
"mj2", "mjpeg", "mjpg", "mka", "mkv", "mlp", "mm", "mmf", "mov", "mp+",
"mp1", "mp2", "mp3", "mp4", "mpc", "mpeg", "mpg", "mpga", "mpp", "mpu",
"mve", "mvi", "mxf", "nc", "nsv", "nut", "nuv", "oga", "ogm", "ogv",
@@ -562,6 +615,12 @@ static const char *const ffmpeg_mime_types[] = {
"video/x-vid",
"video/x-wmv",
"video/x-xvid",
/* special value for the "ffmpeg" input plugin: all streams by
the "ffmpeg" input plugin shall be decoded by this
plugin */
"audio/x-mpd-ffmpeg",
NULL
};
@@ -569,7 +628,7 @@ const struct decoder_plugin ffmpeg_decoder_plugin = {
.name = "ffmpeg",
.init = ffmpeg_init,
.stream_decode = ffmpeg_decode,
.tag_dup = ffmpeg_tag,
.stream_tag = ffmpeg_stream_tag,
.suffixes = ffmpeg_suffixes,
.mime_types = ffmpeg_mime_types
};

114
src/decoder/flac_compat.h Normal file
View File

@@ -0,0 +1,114 @@
/*
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program 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.
*
* This program 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 this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/*
* Common data structures and functions used by FLAC and OggFLAC
*/
#ifndef MPD_FLAC_COMPAT_H
#define MPD_FLAC_COMPAT_H
#include <FLAC/export.h>
#if !defined(FLAC_API_VERSION_CURRENT) || FLAC_API_VERSION_CURRENT <= 7
# include <FLAC/seekable_stream_decoder.h>
/* starting with libFLAC 1.1.3, the SeekableStreamDecoder has been
merged into the StreamDecoder. The following macros try to emulate
the new API for libFLAC 1.1.2 by mapping MPD's StreamDecoder calls
to the old SeekableStreamDecoder API. */
#define FLAC__StreamDecoder FLAC__SeekableStreamDecoder
#define FLAC__stream_decoder_new FLAC__seekable_stream_decoder_new
#define FLAC__stream_decoder_get_decode_position FLAC__seekable_stream_decoder_get_decode_position
#define FLAC__stream_decoder_get_state FLAC__seekable_stream_decoder_get_state
#define FLAC__stream_decoder_process_single FLAC__seekable_stream_decoder_process_single
#define FLAC__stream_decoder_process_until_end_of_metadata FLAC__seekable_stream_decoder_process_until_end_of_metadata
#define FLAC__stream_decoder_seek_absolute FLAC__seekable_stream_decoder_seek_absolute
#define FLAC__stream_decoder_finish FLAC__seekable_stream_decoder_finish
#define FLAC__stream_decoder_delete FLAC__seekable_stream_decoder_delete
#define FLAC__STREAM_DECODER_END_OF_STREAM FLAC__SEEKABLE_STREAM_DECODER_END_OF_STREAM
typedef unsigned flac_read_status_size_t;
#define FLAC__StreamDecoderReadStatus FLAC__SeekableStreamDecoderReadStatus
#define FLAC__STREAM_DECODER_READ_STATUS_CONTINUE FLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_OK
#define FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM FLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_OK
#define FLAC__STREAM_DECODER_READ_STATUS_ABORT FLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_ERROR
#define FLAC__StreamDecoderSeekStatus FLAC__SeekableStreamDecoderSeekStatus
#define FLAC__STREAM_DECODER_SEEK_STATUS_OK FLAC__SEEKABLE_STREAM_DECODER_SEEK_STATUS_OK
#define FLAC__STREAM_DECODER_SEEK_STATUS_ERROR FLAC__SEEKABLE_STREAM_DECODER_SEEK_STATUS_ERROR
#define FLAC__STREAM_DECODER_SEEK_STATUS_UNSUPPORTED FLAC__SEEKABLE_STREAM_DECODER_SEEK_STATUS_ERROR
#define FLAC__StreamDecoderTellStatus FLAC__SeekableStreamDecoderTellStatus
#define FLAC__STREAM_DECODER_TELL_STATUS_OK FLAC__SEEKABLE_STREAM_DECODER_TELL_STATUS_OK
#define FLAC__STREAM_DECODER_TELL_STATUS_ERROR FLAC__SEEKABLE_STREAM_DECODER_TELL_STATUS_ERROR
#define FLAC__STREAM_DECODER_TELL_STATUS_UNSUPPORTED FLAC__SEEKABLE_STREAM_DECODER_TELL_STATUS_ERROR
#define FLAC__StreamDecoderLengthStatus FLAC__SeekableStreamDecoderLengthStatus
#define FLAC__STREAM_DECODER_LENGTH_STATUS_OK FLAC__SEEKABLE_STREAM_DECODER_LENGTH_STATUS_OK
#define FLAC__STREAM_DECODER_LENGTH_STATUS_ERROR FLAC__SEEKABLE_STREAM_DECODER_LENGTH_STATUS_ERROR
#define FLAC__STREAM_DECODER_LENGTH_STATUS_UNSUPPORTED FLAC__SEEKABLE_STREAM_DECODER_LENGTH_STATUS_ERROR
typedef enum {
FLAC__STREAM_DECODER_INIT_STATUS_OK,
FLAC__STREAM_DECODER_INIT_STATUS_ERROR,
} FLAC__StreamDecoderInitStatus;
static inline FLAC__StreamDecoderInitStatus
FLAC__stream_decoder_init_stream(FLAC__SeekableStreamDecoder *decoder,
FLAC__SeekableStreamDecoderReadCallback read_cb,
FLAC__SeekableStreamDecoderSeekCallback seek_cb,
FLAC__SeekableStreamDecoderTellCallback tell_cb,
FLAC__SeekableStreamDecoderLengthCallback length_cb,
FLAC__SeekableStreamDecoderEofCallback eof_cb,
FLAC__SeekableStreamDecoderWriteCallback write_cb,
FLAC__SeekableStreamDecoderMetadataCallback metadata_cb,
FLAC__SeekableStreamDecoderErrorCallback error_cb,
void *data)
{
return FLAC__seekable_stream_decoder_set_read_callback(decoder, read_cb) &&
FLAC__seekable_stream_decoder_set_seek_callback(decoder, seek_cb) &&
FLAC__seekable_stream_decoder_set_tell_callback(decoder, tell_cb) &&
FLAC__seekable_stream_decoder_set_length_callback(decoder, length_cb) &&
FLAC__seekable_stream_decoder_set_eof_callback(decoder, eof_cb) &&
FLAC__seekable_stream_decoder_set_write_callback(decoder, write_cb) &&
FLAC__seekable_stream_decoder_set_metadata_callback(decoder, metadata_cb) &&
FLAC__seekable_stream_decoder_set_metadata_respond(decoder, FLAC__METADATA_TYPE_VORBIS_COMMENT) &&
FLAC__seekable_stream_decoder_set_error_callback(decoder, error_cb) &&
FLAC__seekable_stream_decoder_set_client_data(decoder, data) &&
FLAC__seekable_stream_decoder_init(decoder) == FLAC__SEEKABLE_STREAM_DECODER_OK
? FLAC__STREAM_DECODER_INIT_STATUS_OK
: FLAC__STREAM_DECODER_INIT_STATUS_ERROR;
}
#else /* FLAC_API_VERSION_CURRENT > 7 */
# include <FLAC/stream_decoder.h>
# define flac_init(a,b,c,d,e,f,g,h,i,j) \
(FLAC__stream_decoder_init_stream(a,b,c,d,e,f,g,h,i,j) \
== FLAC__STREAM_DECODER_INIT_STATUS_OK)
typedef size_t flac_read_status_size_t;
#endif /* FLAC_API_VERSION_CURRENT >= 7 */
#endif /* _FLAC_COMMON_H */

View File

@@ -0,0 +1,497 @@
/*
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program 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.
*
* This program 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 this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h" /* must be first for large file support */
#include "_flac_common.h"
#include "flac_compat.h"
#include "flac_metadata.h"
#if defined(FLAC_API_VERSION_CURRENT) && FLAC_API_VERSION_CURRENT > 7
#include "_ogg_common.h"
#endif
#include <glib.h>
#include <assert.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
/* this code was based on flac123, from flac-tools */
static FLAC__StreamDecoderReadStatus
flac_read_cb(G_GNUC_UNUSED const FLAC__StreamDecoder *fd,
FLAC__byte buf[], flac_read_status_size_t *bytes,
void *fdata)
{
struct flac_data *data = fdata;
size_t r;
r = decoder_read(data->decoder, data->input_stream,
(void *)buf, *bytes);
*bytes = r;
if (r == 0) {
if (decoder_get_command(data->decoder) != DECODE_COMMAND_NONE ||
input_stream_eof(data->input_stream))
return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM;
else
return FLAC__STREAM_DECODER_READ_STATUS_ABORT;
}
return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
}
static FLAC__StreamDecoderSeekStatus
flac_seek_cb(G_GNUC_UNUSED const FLAC__StreamDecoder *fd,
FLAC__uint64 offset, void *fdata)
{
struct flac_data *data = (struct flac_data *) fdata;
if (!data->input_stream->seekable)
return FLAC__STREAM_DECODER_SEEK_STATUS_UNSUPPORTED;
if (!input_stream_seek(data->input_stream, offset, SEEK_SET, NULL))
return FLAC__STREAM_DECODER_SEEK_STATUS_ERROR;
return FLAC__STREAM_DECODER_SEEK_STATUS_OK;
}
static FLAC__StreamDecoderTellStatus
flac_tell_cb(G_GNUC_UNUSED const FLAC__StreamDecoder *fd,
FLAC__uint64 * offset, void *fdata)
{
struct flac_data *data = (struct flac_data *) fdata;
if (!data->input_stream->seekable)
return FLAC__STREAM_DECODER_SEEK_STATUS_UNSUPPORTED;
*offset = (long)(data->input_stream->offset);
return FLAC__STREAM_DECODER_TELL_STATUS_OK;
}
static FLAC__StreamDecoderLengthStatus
flac_length_cb(G_GNUC_UNUSED const FLAC__StreamDecoder *fd,
FLAC__uint64 * length, void *fdata)
{
struct flac_data *data = (struct flac_data *) fdata;
if (data->input_stream->size < 0)
return FLAC__STREAM_DECODER_LENGTH_STATUS_UNSUPPORTED;
*length = (size_t) (data->input_stream->size);
return FLAC__STREAM_DECODER_LENGTH_STATUS_OK;
}
static FLAC__bool
flac_eof_cb(G_GNUC_UNUSED const FLAC__StreamDecoder *fd, void *fdata)
{
struct flac_data *data = (struct flac_data *) fdata;
return (decoder_get_command(data->decoder) != DECODE_COMMAND_NONE &&
decoder_get_command(data->decoder) != DECODE_COMMAND_SEEK) ||
input_stream_eof(data->input_stream);
}
static void
flac_error_cb(G_GNUC_UNUSED const FLAC__StreamDecoder *fd,
FLAC__StreamDecoderErrorStatus status, void *fdata)
{
flac_error_common_cb("flac", status, (struct flac_data *) fdata);
}
#if !defined(FLAC_API_VERSION_CURRENT) || FLAC_API_VERSION_CURRENT <= 7
static void flacPrintErroredState(FLAC__SeekableStreamDecoderState state)
{
const char *str = ""; /* "" to silence compiler warning */
switch (state) {
case FLAC__SEEKABLE_STREAM_DECODER_OK:
case FLAC__SEEKABLE_STREAM_DECODER_SEEKING:
case FLAC__SEEKABLE_STREAM_DECODER_END_OF_STREAM:
return;
case FLAC__SEEKABLE_STREAM_DECODER_MEMORY_ALLOCATION_ERROR:
str = "allocation error";
break;
case FLAC__SEEKABLE_STREAM_DECODER_READ_ERROR:
str = "read error";
break;
case FLAC__SEEKABLE_STREAM_DECODER_SEEK_ERROR:
str = "seek error";
break;
case FLAC__SEEKABLE_STREAM_DECODER_STREAM_DECODER_ERROR:
str = "seekable stream error";
break;
case FLAC__SEEKABLE_STREAM_DECODER_ALREADY_INITIALIZED:
str = "decoder already initialized";
break;
case FLAC__SEEKABLE_STREAM_DECODER_INVALID_CALLBACK:
str = "invalid callback";
break;
case FLAC__SEEKABLE_STREAM_DECODER_UNINITIALIZED:
str = "decoder uninitialized";
}
g_warning("%s\n", str);
}
#else /* FLAC_API_VERSION_CURRENT >= 7 */
static void flacPrintErroredState(FLAC__StreamDecoderState state)
{
const char *str = ""; /* "" to silence compiler warning */
switch (state) {
case FLAC__STREAM_DECODER_SEARCH_FOR_METADATA:
case FLAC__STREAM_DECODER_READ_METADATA:
case FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC:
case FLAC__STREAM_DECODER_READ_FRAME:
case FLAC__STREAM_DECODER_END_OF_STREAM:
return;
case FLAC__STREAM_DECODER_OGG_ERROR:
str = "error in the Ogg layer";
break;
case FLAC__STREAM_DECODER_SEEK_ERROR:
str = "seek error";
break;
case FLAC__STREAM_DECODER_ABORTED:
str = "decoder aborted by read";
break;
case FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR:
str = "allocation error";
break;
case FLAC__STREAM_DECODER_UNINITIALIZED:
str = "decoder uninitialized";
}
g_warning("%s\n", str);
}
#endif /* FLAC_API_VERSION_CURRENT >= 7 */
static void flacMetadata(G_GNUC_UNUSED const FLAC__StreamDecoder * dec,
const FLAC__StreamMetadata * block, void *vdata)
{
flac_metadata_common_cb(block, (struct flac_data *) vdata);
}
static FLAC__StreamDecoderWriteStatus
flac_write_cb(const FLAC__StreamDecoder *dec, const FLAC__Frame *frame,
const FLAC__int32 *const buf[], void *vdata)
{
struct flac_data *data = (struct flac_data *) vdata;
FLAC__uint64 nbytes = 0;
if (FLAC__stream_decoder_get_decode_position(dec, &nbytes)) {
if (data->position > 0 && nbytes > data->position) {
nbytes -= data->position;
data->position += nbytes;
} else {
data->position = nbytes;
nbytes = 0;
}
} else
nbytes = 0;
return flac_common_write(data, frame, buf, nbytes);
}
static struct tag *
flac_tag_dup(const char *file)
{
return flac_tag_load(file, NULL);
}
/**
* Some glue code around FLAC__stream_decoder_new().
*/
static FLAC__StreamDecoder *
flac_decoder_new(void)
{
FLAC__StreamDecoder *sd = FLAC__stream_decoder_new();
if (sd == NULL) {
g_warning("FLAC__stream_decoder_new() failed");
return NULL;
}
#if defined(FLAC_API_VERSION_CURRENT) && FLAC_API_VERSION_CURRENT > 7
if(!FLAC__stream_decoder_set_metadata_respond(sd, FLAC__METADATA_TYPE_VORBIS_COMMENT))
g_debug("FLAC__stream_decoder_set_metadata_respond() has failed");
#endif
return sd;
}
static bool
flac_decoder_initialize(struct flac_data *data, FLAC__StreamDecoder *sd,
FLAC__uint64 duration)
{
data->total_frames = duration;
if (!FLAC__stream_decoder_process_until_end_of_metadata(sd)) {
g_warning("problem reading metadata");
return false;
}
if (data->initialized) {
/* done */
decoder_initialized(data->decoder, &data->audio_format,
data->input_stream->seekable,
(float)data->total_frames /
(float)data->audio_format.sample_rate);
return true;
}
if (data->input_stream->seekable)
/* allow the workaround below only for nonseekable
streams*/
return false;
/* no stream_info packet found; try to initialize the decoder
from the first frame header */
FLAC__stream_decoder_process_single(sd);
return data->initialized;
}
static void
flac_decoder_loop(struct flac_data *data, FLAC__StreamDecoder *flac_dec,
FLAC__uint64 t_start, FLAC__uint64 t_end)
{
struct decoder *decoder = data->decoder;
enum decoder_command cmd;
data->first_frame = t_start;
while (true) {
if (data->tag != NULL && !tag_is_empty(data->tag)) {
cmd = decoder_tag(data->decoder, data->input_stream,
data->tag);
tag_free(data->tag);
data->tag = tag_new();
} else
cmd = decoder_get_command(decoder);
if (cmd == DECODE_COMMAND_SEEK) {
FLAC__uint64 seek_sample = t_start +
decoder_seek_where(decoder) *
data->audio_format.sample_rate;
if (seek_sample >= t_start &&
(t_end == 0 || seek_sample <= t_end) &&
FLAC__stream_decoder_seek_absolute(flac_dec, seek_sample)) {
data->next_frame = seek_sample;
data->position = 0;
decoder_command_finished(decoder);
} else
decoder_seek_error(decoder);
} else if (cmd == DECODE_COMMAND_STOP ||
FLAC__stream_decoder_get_state(flac_dec) == FLAC__STREAM_DECODER_END_OF_STREAM)
break;
if (t_end != 0 && data->next_frame >= t_end)
/* end of this sub track */
break;
if (!FLAC__stream_decoder_process_single(flac_dec)) {
cmd = decoder_get_command(decoder);
if (cmd != DECODE_COMMAND_SEEK)
break;
}
}
if (cmd != DECODE_COMMAND_STOP) {
flacPrintErroredState(FLAC__stream_decoder_get_state(flac_dec));
FLAC__stream_decoder_finish(flac_dec);
}
}
static void
flac_decode_internal(struct decoder * decoder,
struct input_stream *input_stream,
bool is_ogg)
{
FLAC__StreamDecoder *flac_dec;
struct flac_data data;
const char *err = NULL;
flac_dec = flac_decoder_new();
if (flac_dec == NULL)
return;
flac_data_init(&data, decoder, input_stream);
data.tag = tag_new();
if (is_ogg) {
#if defined(FLAC_API_VERSION_CURRENT) && FLAC_API_VERSION_CURRENT > 7
FLAC__StreamDecoderInitStatus status =
FLAC__stream_decoder_init_ogg_stream(flac_dec,
flac_read_cb,
flac_seek_cb,
flac_tell_cb,
flac_length_cb,
flac_eof_cb,
flac_write_cb,
flacMetadata,
flac_error_cb,
(void *)&data);
if (status != FLAC__STREAM_DECODER_INIT_STATUS_OK) {
err = "doing Ogg init()";
goto fail;
}
#else
goto fail;
#endif
} else {
FLAC__StreamDecoderInitStatus status =
FLAC__stream_decoder_init_stream(flac_dec,
flac_read_cb,
flac_seek_cb,
flac_tell_cb,
flac_length_cb,
flac_eof_cb,
flac_write_cb,
flacMetadata,
flac_error_cb,
(void *)&data);
if (status != FLAC__STREAM_DECODER_INIT_STATUS_OK) {
err = "doing init()";
goto fail;
}
}
if (!flac_decoder_initialize(&data, flac_dec, 0)) {
flac_data_deinit(&data);
FLAC__stream_decoder_delete(flac_dec);
return;
}
flac_decoder_loop(&data, flac_dec, 0, 0);
fail:
flac_data_deinit(&data);
FLAC__stream_decoder_delete(flac_dec);
if (err)
g_warning("%s\n", err);
}
static void
flac_decode(struct decoder * decoder, struct input_stream *input_stream)
{
flac_decode_internal(decoder, input_stream, false);
}
#ifndef HAVE_OGGFLAC
static bool
oggflac_init(G_GNUC_UNUSED const struct config_param *param)
{
#if defined(FLAC_API_VERSION_CURRENT) && FLAC_API_VERSION_CURRENT > 7
return !!FLAC_API_SUPPORTS_OGG_FLAC;
#else
/* disable oggflac when libflac is too old */
return false;
#endif
}
#if defined(FLAC_API_VERSION_CURRENT) && FLAC_API_VERSION_CURRENT > 7
static struct tag *
oggflac_tag_dup(const char *file)
{
struct tag *ret = NULL;
FLAC__Metadata_Iterator *it;
FLAC__StreamMetadata *block;
FLAC__Metadata_Chain *chain = FLAC__metadata_chain_new();
if (!(FLAC__metadata_chain_read_ogg(chain, file)))
goto out;
it = FLAC__metadata_iterator_new();
FLAC__metadata_iterator_init(it, chain);
ret = tag_new();
do {
if (!(block = FLAC__metadata_iterator_get_block(it)))
break;
flac_tag_apply_metadata(ret, NULL, block);
} while (FLAC__metadata_iterator_next(it));
FLAC__metadata_iterator_delete(it);
if (!tag_is_defined(ret)) {
tag_free(ret);
ret = NULL;
}
out:
FLAC__metadata_chain_delete(chain);
return ret;
}
static void
oggflac_decode(struct decoder *decoder, struct input_stream *input_stream)
{
if (ogg_stream_type_detect(input_stream) != FLAC)
return;
/* rewind the stream, because ogg_stream_type_detect() has
moved it */
input_stream_seek(input_stream, 0, SEEK_SET, NULL);
flac_decode_internal(decoder, input_stream, true);
}
static const char *const oggflac_suffixes[] = { "ogg", "oga", NULL };
static const char *const oggflac_mime_types[] = {
"application/ogg",
"application/x-ogg",
"audio/ogg",
"audio/x-flac+ogg",
"audio/x-ogg",
NULL
};
#endif /* FLAC_API_VERSION_CURRENT >= 7 */
const struct decoder_plugin oggflac_decoder_plugin = {
.name = "oggflac",
.init = oggflac_init,
#if defined(FLAC_API_VERSION_CURRENT) && FLAC_API_VERSION_CURRENT > 7
.stream_decode = oggflac_decode,
.tag_dup = oggflac_tag_dup,
.suffixes = oggflac_suffixes,
.mime_types = oggflac_mime_types
#endif
};
#endif /* HAVE_OGGFLAC */
static const char *const flac_suffixes[] = { "flac", NULL };
static const char *const flac_mime_types[] = {
"application/flac",
"application/x-flac",
"audio/flac",
"audio/x-flac",
NULL
};
const struct decoder_plugin flac_decoder_plugin = {
.name = "flac",
.stream_decode = flac_decode,
.tag_dup = flac_tag_dup,
.suffixes = flac_suffixes,
.mime_types = flac_mime_types,
};

286
src/decoder/flac_metadata.c Normal file
View File

@@ -0,0 +1,286 @@
/*
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program 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.
*
* This program 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 this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h"
#include "flac_metadata.h"
#include "replay_gain_info.h"
#include "tag.h"
#include <glib.h>
#include <assert.h>
#include <stdbool.h>
#include <stdlib.h>
static bool
flac_find_float_comment(const FLAC__StreamMetadata *block,
const char *cmnt, float *fl)
{
int offset;
size_t pos;
int len;
unsigned char tmp, *p;
offset = FLAC__metadata_object_vorbiscomment_find_entry_from(block, 0,
cmnt);
if (offset < 0)
return false;
pos = strlen(cmnt) + 1; /* 1 is for '=' */
len = block->data.vorbis_comment.comments[offset].length - pos;
if (len <= 0)
return false;
p = &block->data.vorbis_comment.comments[offset].entry[pos];
tmp = p[len];
p[len] = '\0';
*fl = (float)atof((char *)p);
p[len] = tmp;
return true;
}
bool
flac_parse_replay_gain(struct replay_gain_info *rgi,
const FLAC__StreamMetadata *block)
{
bool found = false;
replay_gain_info_init(rgi);
if (flac_find_float_comment(block, "replaygain_album_gain",
&rgi->tuples[REPLAY_GAIN_ALBUM].gain))
found = true;
if (flac_find_float_comment(block, "replaygain_album_peak",
&rgi->tuples[REPLAY_GAIN_ALBUM].peak))
found = true;
if (flac_find_float_comment(block, "replaygain_track_gain",
&rgi->tuples[REPLAY_GAIN_TRACK].gain))
found = true;
if (flac_find_float_comment(block, "replaygain_track_peak",
&rgi->tuples[REPLAY_GAIN_TRACK].peak))
found = true;
return found;
}
static bool
flac_find_string_comment(const FLAC__StreamMetadata *block,
const char *cmnt, char **str)
{
int offset;
size_t pos;
int len;
const unsigned char *p;
*str = NULL;
offset = FLAC__metadata_object_vorbiscomment_find_entry_from(block, 0,
cmnt);
if (offset < 0)
return false;
pos = strlen(cmnt) + 1; /* 1 is for '=' */
len = block->data.vorbis_comment.comments[offset].length - pos;
if (len <= 0)
return false;
p = &block->data.vorbis_comment.comments[offset].entry[pos];
*str = g_strndup((const char *)p, len);
return true;
}
bool
flac_parse_mixramp(char **mixramp_start, char **mixramp_end,
const FLAC__StreamMetadata *block)
{
bool found = false;
if (flac_find_string_comment(block, "mixramp_start", mixramp_start))
found = true;
if (flac_find_string_comment(block, "mixramp_end", mixramp_end))
found = true;
return found;
}
/**
* Checks if the specified name matches the entry's name, and if yes,
* returns the comment value (not null-temrinated).
*/
static const char *
flac_comment_value(const FLAC__StreamMetadata_VorbisComment_Entry *entry,
const char *name, const char *char_tnum, size_t *length_r)
{
size_t name_length = strlen(name);
size_t char_tnum_length = 0;
const char *comment = (const char*)entry->entry;
if (entry->length <= name_length ||
g_ascii_strncasecmp(comment, name, name_length) != 0)
return NULL;
if (char_tnum != NULL) {
char_tnum_length = strlen(char_tnum);
if (entry->length > name_length + char_tnum_length + 2 &&
comment[name_length] == '[' &&
g_ascii_strncasecmp(comment + name_length + 1,
char_tnum, char_tnum_length) == 0 &&
comment[name_length + char_tnum_length + 1] == ']')
name_length = name_length + char_tnum_length + 2;
else if (entry->length > name_length + char_tnum_length &&
g_ascii_strncasecmp(comment + name_length,
char_tnum, char_tnum_length) == 0)
name_length = name_length + char_tnum_length;
}
if (comment[name_length] == '=') {
*length_r = entry->length - name_length - 1;
return comment + name_length + 1;
}
return NULL;
}
/**
* Check if the comment's name equals the passed name, and if so, copy
* the comment value into the tag.
*/
static bool
flac_copy_comment(struct tag *tag,
const FLAC__StreamMetadata_VorbisComment_Entry *entry,
const char *name, enum tag_type tag_type,
const char *char_tnum)
{
const char *value;
size_t value_length;
value = flac_comment_value(entry, name, char_tnum, &value_length);
if (value != NULL) {
tag_add_item_n(tag, tag_type, value, value_length);
return true;
}
return false;
}
/* tracknumber is used in VCs, MPD uses "track" ..., all the other
* tag names match */
static const char *VORBIS_COMMENT_TRACK_KEY = "tracknumber";
static const char *VORBIS_COMMENT_DISC_KEY = "discnumber";
static void
flac_parse_comment(struct tag *tag, const char *char_tnum,
const FLAC__StreamMetadata_VorbisComment_Entry *entry)
{
assert(tag != NULL);
if (flac_copy_comment(tag, entry, VORBIS_COMMENT_TRACK_KEY,
TAG_TRACK, char_tnum) ||
flac_copy_comment(tag, entry, VORBIS_COMMENT_DISC_KEY,
TAG_DISC, char_tnum) ||
flac_copy_comment(tag, entry, "album artist",
TAG_ALBUM_ARTIST, char_tnum))
return;
for (unsigned i = 0; i < TAG_NUM_OF_ITEM_TYPES; ++i)
if (flac_copy_comment(tag, entry,
tag_item_names[i], i, char_tnum))
return;
}
void
flac_vorbis_comments_to_tag(struct tag *tag, const char *char_tnum,
const FLAC__StreamMetadata_VorbisComment *comment)
{
for (unsigned i = 0; i < comment->num_comments; ++i)
flac_parse_comment(tag, char_tnum, &comment->comments[i]);
}
void
flac_tag_apply_metadata(struct tag *tag, const char *track,
const FLAC__StreamMetadata *block)
{
switch (block->type) {
case FLAC__METADATA_TYPE_VORBIS_COMMENT:
flac_vorbis_comments_to_tag(tag, track,
&block->data.vorbis_comment);
break;
case FLAC__METADATA_TYPE_STREAMINFO:
tag->time = flac_duration(&block->data.stream_info);
break;
default:
break;
}
}
struct tag *
flac_tag_load(const char *file, const char *char_tnum)
{
struct tag *tag;
FLAC__Metadata_SimpleIterator *it;
FLAC__StreamMetadata *block = NULL;
it = FLAC__metadata_simple_iterator_new();
if (!FLAC__metadata_simple_iterator_init(it, file, 1, 0)) {
const char *err;
FLAC_API FLAC__Metadata_SimpleIteratorStatus s;
s = FLAC__metadata_simple_iterator_status(it);
switch (s) { /* slightly more human-friendly messages: */
case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ILLEGAL_INPUT:
err = "illegal input";
break;
case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ERROR_OPENING_FILE:
err = "error opening file";
break;
case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_NOT_A_FLAC_FILE:
err = "not a FLAC file";
break;
default:
err = FLAC__Metadata_SimpleIteratorStatusString[s];
}
g_debug("Reading '%s' metadata gave the following error: %s\n",
file, err);
FLAC__metadata_simple_iterator_delete(it);
return NULL;
}
tag = tag_new();
do {
block = FLAC__metadata_simple_iterator_get_block(it);
if (!block)
break;
flac_tag_apply_metadata(tag, char_tnum, block);
FLAC__metadata_object_delete(block);
} while (FLAC__metadata_simple_iterator_next(it));
FLAC__metadata_simple_iterator_delete(it);
if (!tag_is_defined(tag)) {
tag_free(tag);
tag = NULL;
}
return tag;
}

View File

@@ -0,0 +1,55 @@
/*
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program 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.
*
* This program 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 this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef MPD_FLAC_METADATA_H
#define MPD_FLAC_METADATA_H
#include <stdbool.h>
#include <FLAC/metadata.h>
struct tag;
struct replay_gain_info;
static inline unsigned
flac_duration(const FLAC__StreamMetadata_StreamInfo *stream_info)
{
return (stream_info->total_samples + stream_info->sample_rate - 1) /
stream_info->sample_rate;
}
bool
flac_parse_replay_gain(struct replay_gain_info *rgi,
const FLAC__StreamMetadata *block);
bool
flac_parse_mixramp(char **mixramp_start, char **mixramp_end,
const FLAC__StreamMetadata *block);
void
flac_vorbis_comments_to_tag(struct tag *tag, const char *char_tnum,
const FLAC__StreamMetadata_VorbisComment *comment);
void
flac_tag_apply_metadata(struct tag *tag, const char *track,
const FLAC__StreamMetadata *block);
struct tag *
flac_tag_load(const char *file, const char *char_tnum);
#endif

109
src/decoder/flac_pcm.c Normal file
View File

@@ -0,0 +1,109 @@
/*
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program 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.
*
* This program 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 this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h"
#include "flac_pcm.h"
#include <assert.h>
static void flac_convert_stereo16(int16_t *dest,
const FLAC__int32 * const buf[],
unsigned int position, unsigned int end)
{
for (; position < end; ++position) {
*dest++ = buf[0][position];
*dest++ = buf[1][position];
}
}
static void
flac_convert_16(int16_t *dest,
unsigned int num_channels,
const FLAC__int32 * const buf[],
unsigned int position, unsigned int end)
{
unsigned int c_chan;
for (; position < end; ++position)
for (c_chan = 0; c_chan < num_channels; c_chan++)
*dest++ = buf[c_chan][position];
}
/**
* Note: this function also handles 24 bit files!
*/
static void
flac_convert_32(int32_t *dest,
unsigned int num_channels,
const FLAC__int32 * const buf[],
unsigned int position, unsigned int end)
{
unsigned int c_chan;
for (; position < end; ++position)
for (c_chan = 0; c_chan < num_channels; c_chan++)
*dest++ = buf[c_chan][position];
}
static void
flac_convert_8(int8_t *dest,
unsigned int num_channels,
const FLAC__int32 * const buf[],
unsigned int position, unsigned int end)
{
unsigned int c_chan;
for (; position < end; ++position)
for (c_chan = 0; c_chan < num_channels; c_chan++)
*dest++ = buf[c_chan][position];
}
void
flac_convert(void *dest,
unsigned int num_channels, enum sample_format sample_format,
const FLAC__int32 *const buf[],
unsigned int position, unsigned int end)
{
switch (sample_format) {
case SAMPLE_FORMAT_S16:
if (num_channels == 2)
flac_convert_stereo16((int16_t*)dest, buf,
position, end);
else
flac_convert_16((int16_t*)dest, num_channels, buf,
position, end);
break;
case SAMPLE_FORMAT_S24_P32:
case SAMPLE_FORMAT_S32:
flac_convert_32((int32_t*)dest, num_channels, buf,
position, end);
break;
case SAMPLE_FORMAT_S8:
flac_convert_8((int8_t*)dest, num_channels, buf,
position, end);
break;
case SAMPLE_FORMAT_S24:
case SAMPLE_FORMAT_UNDEFINED:
/* unreachable */
assert(false);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2003-2009 The Music Player Daemon Project
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,33 +17,17 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "normalize.h"
#include "compress.h"
#include "conf.h"
#ifndef MPD_FLAC_PCM_H
#define MPD_FLAC_PCM_H
#include "audio_format.h"
#define DEFAULT_VOLUME_NORMALIZATION 0
#include <FLAC/ordinals.h>
int normalizationEnabled;
void
flac_convert(void *dest,
unsigned int num_channels, enum sample_format sample_format,
const FLAC__int32 *const buf[],
unsigned int position, unsigned int end);
void initNormalization(void)
{
normalizationEnabled = config_get_bool(CONF_VOLUME_NORMALIZATION,
DEFAULT_VOLUME_NORMALIZATION);
if (normalizationEnabled)
CompressCfg(0, ANTICLIP, TARGET, GAINMAX, GAINSMOOTH, BUCKETS);
}
void finishNormalization(void)
{
if (normalizationEnabled) CompressFree();
}
void normalizeData(char *buffer, int bufferSize,
const struct audio_format *format)
{
if ((format->bits != 16) || (format->channels != 2)) return;
CompressDo(buffer, bufferSize);
}
#endif

View File

@@ -1,918 +0,0 @@
/*
* Copyright (C) 2003-2009 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program 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.
*
* This program 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 this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "_flac_common.h"
#include <glib.h>
#include <assert.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#ifdef HAVE_CUE /* libcue */
#include "../cue/cue_tag.h"
#endif
/* this code was based on flac123, from flac-tools */
static flac_read_status
flac_read_cb(G_GNUC_UNUSED const flac_decoder *fd,
FLAC__byte buf[], flac_read_status_size_t *bytes,
void *fdata)
{
struct flac_data *data = fdata;
size_t r;
r = decoder_read(data->decoder, data->input_stream,
(void *)buf, *bytes);
*bytes = r;
if (r == 0) {
if (decoder_get_command(data->decoder) != DECODE_COMMAND_NONE ||
input_stream_eof(data->input_stream))
return flac_read_status_eof;
else
return flac_read_status_abort;
}
return flac_read_status_continue;
}
static flac_seek_status
flac_seek_cb(G_GNUC_UNUSED const flac_decoder *fd,
FLAC__uint64 offset, void *fdata)
{
struct flac_data *data = (struct flac_data *) fdata;
if (!input_stream_seek(data->input_stream, offset, SEEK_SET))
return flac_seek_status_error;
return flac_seek_status_ok;
}
static flac_tell_status
flac_tell_cb(G_GNUC_UNUSED const flac_decoder *fd,
FLAC__uint64 * offset, void *fdata)
{
struct flac_data *data = (struct flac_data *) fdata;
*offset = (long)(data->input_stream->offset);
return flac_tell_status_ok;
}
static flac_length_status
flac_length_cb(G_GNUC_UNUSED const flac_decoder *fd,
FLAC__uint64 * length, void *fdata)
{
struct flac_data *data = (struct flac_data *) fdata;
if (data->input_stream->size < 0)
return flac_length_status_unsupported;
*length = (size_t) (data->input_stream->size);
return flac_length_status_ok;
}
static FLAC__bool
flac_eof_cb(G_GNUC_UNUSED const flac_decoder *fd, void *fdata)
{
struct flac_data *data = (struct flac_data *) fdata;
return (decoder_get_command(data->decoder) != DECODE_COMMAND_NONE &&
decoder_get_command(data->decoder) != DECODE_COMMAND_SEEK) ||
input_stream_eof(data->input_stream);
}
static void
flac_error_cb(G_GNUC_UNUSED const flac_decoder *fd,
FLAC__StreamDecoderErrorStatus status, void *fdata)
{
flac_error_common_cb("flac", status, (struct flac_data *) fdata);
}
#if !defined(FLAC_API_VERSION_CURRENT) || FLAC_API_VERSION_CURRENT <= 7
static void flacPrintErroredState(FLAC__SeekableStreamDecoderState state)
{
const char *str = ""; /* "" to silence compiler warning */
switch (state) {
case FLAC__SEEKABLE_STREAM_DECODER_OK:
case FLAC__SEEKABLE_STREAM_DECODER_SEEKING:
case FLAC__SEEKABLE_STREAM_DECODER_END_OF_STREAM:
return;
case FLAC__SEEKABLE_STREAM_DECODER_MEMORY_ALLOCATION_ERROR:
str = "allocation error";
break;
case FLAC__SEEKABLE_STREAM_DECODER_READ_ERROR:
str = "read error";
break;
case FLAC__SEEKABLE_STREAM_DECODER_SEEK_ERROR:
str = "seek error";
break;
case FLAC__SEEKABLE_STREAM_DECODER_STREAM_DECODER_ERROR:
str = "seekable stream error";
break;
case FLAC__SEEKABLE_STREAM_DECODER_ALREADY_INITIALIZED:
str = "decoder already initialized";
break;
case FLAC__SEEKABLE_STREAM_DECODER_INVALID_CALLBACK:
str = "invalid callback";
break;
case FLAC__SEEKABLE_STREAM_DECODER_UNINITIALIZED:
str = "decoder uninitialized";
}
g_warning("%s\n", str);
}
static bool
flac_init(FLAC__SeekableStreamDecoder *dec,
FLAC__SeekableStreamDecoderReadCallback read_cb,
FLAC__SeekableStreamDecoderSeekCallback seek_cb,
FLAC__SeekableStreamDecoderTellCallback tell_cb,
FLAC__SeekableStreamDecoderLengthCallback length_cb,
FLAC__SeekableStreamDecoderEofCallback eof_cb,
FLAC__SeekableStreamDecoderWriteCallback write_cb,
FLAC__SeekableStreamDecoderMetadataCallback metadata_cb,
FLAC__SeekableStreamDecoderErrorCallback error_cb,
void *data)
{
return FLAC__seekable_stream_decoder_set_read_callback(dec, read_cb) &&
FLAC__seekable_stream_decoder_set_seek_callback(dec, seek_cb) &&
FLAC__seekable_stream_decoder_set_tell_callback(dec, tell_cb) &&
FLAC__seekable_stream_decoder_set_length_callback(dec, length_cb) &&
FLAC__seekable_stream_decoder_set_eof_callback(dec, eof_cb) &&
FLAC__seekable_stream_decoder_set_write_callback(dec, write_cb) &&
FLAC__seekable_stream_decoder_set_metadata_callback(dec, metadata_cb) &&
FLAC__seekable_stream_decoder_set_metadata_respond(dec, FLAC__METADATA_TYPE_VORBIS_COMMENT) &&
FLAC__seekable_stream_decoder_set_error_callback(dec, error_cb) &&
FLAC__seekable_stream_decoder_set_client_data(dec, data) &&
FLAC__seekable_stream_decoder_init(dec) == FLAC__SEEKABLE_STREAM_DECODER_OK;
}
#else /* FLAC_API_VERSION_CURRENT >= 7 */
static void flacPrintErroredState(FLAC__StreamDecoderState state)
{
const char *str = ""; /* "" to silence compiler warning */
switch (state) {
case FLAC__STREAM_DECODER_SEARCH_FOR_METADATA:
case FLAC__STREAM_DECODER_READ_METADATA:
case FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC:
case FLAC__STREAM_DECODER_READ_FRAME:
case FLAC__STREAM_DECODER_END_OF_STREAM:
return;
case FLAC__STREAM_DECODER_OGG_ERROR:
str = "error in the Ogg layer";
break;
case FLAC__STREAM_DECODER_SEEK_ERROR:
str = "seek error";
break;
case FLAC__STREAM_DECODER_ABORTED:
str = "decoder aborted by read";
break;
case FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR:
str = "allocation error";
break;
case FLAC__STREAM_DECODER_UNINITIALIZED:
str = "decoder uninitialized";
}
g_warning("%s\n", str);
}
#endif /* FLAC_API_VERSION_CURRENT >= 7 */
static void flacMetadata(G_GNUC_UNUSED const flac_decoder * dec,
const FLAC__StreamMetadata * block, void *vdata)
{
flac_metadata_common_cb(block, (struct flac_data *) vdata);
}
static FLAC__StreamDecoderWriteStatus
flac_write_cb(const flac_decoder *dec, const FLAC__Frame *frame,
const FLAC__int32 *const buf[], void *vdata)
{
FLAC__uint32 samples = frame->header.blocksize;
struct flac_data *data = (struct flac_data *) vdata;
float timeChange;
FLAC__uint64 newPosition = 0;
timeChange = ((float)samples) / frame->header.sample_rate;
data->time += timeChange;
flac_get_decode_position(dec, &newPosition);
if (data->position && newPosition >= data->position) {
assert(timeChange >= 0);
data->bit_rate =
((newPosition - data->position) * 8.0 / timeChange)
/ 1000 + 0.5;
}
data->position = newPosition;
return flac_common_write(data, frame, buf);
}
static struct tag *
flac_tag_load(const char *file, const char *char_tnum)
{
struct tag *tag;
FLAC__Metadata_SimpleIterator *it;
FLAC__StreamMetadata *block = NULL;
it = FLAC__metadata_simple_iterator_new();
if (!FLAC__metadata_simple_iterator_init(it, file, 1, 0)) {
const char *err;
FLAC_API FLAC__Metadata_SimpleIteratorStatus s;
s = FLAC__metadata_simple_iterator_status(it);
switch (s) { /* slightly more human-friendly messages: */
case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ILLEGAL_INPUT:
err = "illegal input";
break;
case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ERROR_OPENING_FILE:
err = "error opening file";
break;
case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_NOT_A_FLAC_FILE:
err = "not a FLAC file";
break;
default:
err = FLAC__Metadata_SimpleIteratorStatusString[s];
}
g_debug("Reading '%s' metadata gave the following error: %s\n",
file, err);
FLAC__metadata_simple_iterator_delete(it);
return NULL;
}
tag = tag_new();
do {
block = FLAC__metadata_simple_iterator_get_block(it);
if (!block)
break;
if (block->type == FLAC__METADATA_TYPE_VORBIS_COMMENT) {
flac_vorbis_comments_to_tag(tag, char_tnum, block);
} else if (block->type == FLAC__METADATA_TYPE_STREAMINFO) {
tag->time = ((float)block->data.stream_info.total_samples) /
block->data.stream_info.sample_rate + 0.5;
}
FLAC__metadata_object_delete(block);
} while (FLAC__metadata_simple_iterator_next(it));
FLAC__metadata_simple_iterator_delete(it);
if (!tag_is_defined(tag)) {
tag_free(tag);
tag = NULL;
}
return tag;
}
#if defined(FLAC_API_VERSION_CURRENT) && FLAC_API_VERSION_CURRENT > 7
static struct tag *
flac_cue_tag_load(const char *file)
{
struct tag* tag = NULL;
char* char_tnum = NULL;
char* ptr = NULL;
unsigned int tnum = 0;
unsigned int sample_rate = 0;
FLAC__uint64 track_time = 0;
#ifdef HAVE_CUE /* libcue */
FLAC__StreamMetadata* vc;
#endif /* libcue */
FLAC__StreamMetadata* si = FLAC__metadata_object_new(FLAC__METADATA_TYPE_STREAMINFO);
FLAC__StreamMetadata* cs;
tnum = flac_vtrack_tnum(file);
char_tnum = g_strdup_printf("%u", tnum);
ptr = strrchr(file, '/');
*ptr = '\0';
#ifdef HAVE_CUE /* libcue */
if (FLAC__metadata_get_tags(file, &vc))
{
for (unsigned i = 0; i < vc->data.vorbis_comment.num_comments;
i++)
{
if ((ptr = (char*)vc->data.vorbis_comment.comments[i].entry) != NULL)
{
if (g_ascii_strncasecmp(ptr, "cuesheet", 8) == 0)
{
while (*(++ptr) != '=');
tag = cue_tag_string( ++ptr,
tnum);
}
}
}
FLAC__metadata_object_delete(vc);
}
#endif /* libcue */
if (tag == NULL)
tag = flac_tag_load(file, char_tnum);
if (char_tnum != NULL)
{
tag_add_item( tag,
TAG_ITEM_TRACK,
char_tnum);
g_free(char_tnum);
}
if (FLAC__metadata_get_streaminfo(file, si))
{
sample_rate = si->data.stream_info.sample_rate;
FLAC__metadata_object_delete(si);
}
if (FLAC__metadata_get_cuesheet(file, &cs))
{
if (cs->data.cue_sheet.tracks != NULL
&& (tnum <= cs->data.cue_sheet.num_tracks - 1))
{
track_time = cs->data.cue_sheet.tracks[tnum].offset
- cs->data.cue_sheet.tracks[tnum - 1].offset;
}
FLAC__metadata_object_delete(cs);
}
if (sample_rate != 0)
{
tag->time = (unsigned int)(track_time/sample_rate);
}
return tag;
}
#endif /* FLAC_API_VERSION_CURRENT >= 7 */
static struct tag *
flac_tag_dup(const char *file)
{
#if defined(FLAC_API_VERSION_CURRENT) && FLAC_API_VERSION_CURRENT > 7
struct stat st;
if (stat(file, &st) < 0)
return flac_cue_tag_load(file);
else
#endif /* FLAC_API_VERSION_CURRENT >= 7 */
return flac_tag_load(file, NULL);
}
static void
flac_decode_internal(struct decoder * decoder,
struct input_stream *input_stream,
bool is_ogg)
{
flac_decoder *flac_dec;
struct flac_data data;
enum decoder_command cmd;
const char *err = NULL;
if (!(flac_dec = flac_new()))
return;
flac_data_init(&data, decoder, input_stream);
data.tag = tag_new();
#if defined(FLAC_API_VERSION_CURRENT) && FLAC_API_VERSION_CURRENT > 7
if(!FLAC__stream_decoder_set_metadata_respond(flac_dec, FLAC__METADATA_TYPE_VORBIS_COMMENT))
{
g_debug("Failed to set metadata respond\n");
}
#endif
if (is_ogg) {
if (!flac_ogg_init(flac_dec, flac_read_cb,
flac_seek_cb, flac_tell_cb,
flac_length_cb, flac_eof_cb,
flac_write_cb, flacMetadata,
flac_error_cb, (void *)&data)) {
err = "doing Ogg init()";
goto fail;
}
} else {
if (!flac_init(flac_dec, flac_read_cb,
flac_seek_cb, flac_tell_cb,
flac_length_cb, flac_eof_cb,
flac_write_cb, flacMetadata,
flac_error_cb, (void *)&data)) {
err = "doing init()";
goto fail;
}
}
if (!flac_process_metadata(flac_dec)) {
err = "problem reading metadata";
goto fail;
}
if (!audio_format_valid(&data.audio_format)) {
g_warning("Invalid audio format: %u:%u:%u\n",
data.audio_format.sample_rate,
data.audio_format.bits,
data.audio_format.channels);
goto fail;
}
decoder_initialized(decoder, &data.audio_format,
input_stream->seekable, data.total_time);
while (true) {
if (!tag_is_empty(data.tag)) {
cmd = decoder_tag(decoder, input_stream, data.tag);
tag_free(data.tag);
data.tag = tag_new();
} else
cmd = decoder_get_command(decoder);
if (cmd == DECODE_COMMAND_SEEK) {
FLAC__uint64 seek_sample = decoder_seek_where(decoder) *
data.audio_format.sample_rate + 0.5;
if (flac_seek_absolute(flac_dec, seek_sample)) {
data.time = ((float)seek_sample) /
data.audio_format.sample_rate;
data.position = 0;
decoder_command_finished(decoder);
} else
decoder_seek_error(decoder);
} else if (cmd == DECODE_COMMAND_STOP ||
flac_get_state(flac_dec) == flac_decoder_eof)
break;
if (!flac_process_single(flac_dec)) {
cmd = decoder_get_command(decoder);
if (cmd != DECODE_COMMAND_SEEK)
break;
}
}
if (cmd != DECODE_COMMAND_STOP) {
flacPrintErroredState(flac_get_state(flac_dec));
flac_finish(flac_dec);
}
fail:
if (data.replay_gain_info)
replay_gain_info_free(data.replay_gain_info);
tag_free(data.tag);
if (flac_dec)
flac_delete(flac_dec);
if (err)
g_warning("%s\n", err);
}
static void
flac_decode(struct decoder * decoder, struct input_stream *input_stream)
{
flac_decode_internal(decoder, input_stream, false);
}
#if defined(FLAC_API_VERSION_CURRENT) && FLAC_API_VERSION_CURRENT > 7
/**
* @brief Decode a flac file with embedded cue sheets
* @param const char* fname filename on fs
*/
static void
flac_container_decode(struct decoder* decoder,
const char* fname,
bool is_ogg)
{
unsigned int tnum = 0;
FLAC__uint64 t_start = 0;
FLAC__uint64 t_end = 0;
FLAC__uint64 track_time = 0;
FLAC__StreamMetadata* cs = NULL;
flac_decoder *flac_dec;
struct flac_data data;
const char *err = NULL;
char* pathname = g_strdup(fname);
char* slash = strrchr(pathname, '/');
*slash = '\0';
tnum = flac_vtrack_tnum(fname);
cs = FLAC__metadata_object_new(FLAC__METADATA_TYPE_CUESHEET);
FLAC__metadata_get_cuesheet(pathname, &cs);
if (cs != NULL)
{
if (cs->data.cue_sheet.tracks != NULL
&& (tnum <= cs->data.cue_sheet.num_tracks - 1))
{
t_start = cs->data.cue_sheet.tracks[tnum - 1].offset;
t_end = cs->data.cue_sheet.tracks[tnum].offset;
track_time = cs->data.cue_sheet.tracks[tnum].offset
- cs->data.cue_sheet.tracks[tnum - 1].offset;
}
FLAC__metadata_object_delete(cs);
}
else
{
g_free(pathname);
return;
}
if (!(flac_dec = flac_new()))
{
g_free(pathname);
return;
}
flac_data_init(&data, decoder, NULL);
#if defined(FLAC_API_VERSION_CURRENT) && FLAC_API_VERSION_CURRENT > 7
if(!FLAC__stream_decoder_set_metadata_respond(flac_dec, FLAC__METADATA_TYPE_VORBIS_COMMENT))
{
g_debug("Failed to set metadata respond\n");
}
#endif
if (is_ogg)
{
if (FLAC__stream_decoder_init_ogg_file( flac_dec,
pathname,
flac_write_cb,
flacMetadata,
flac_error_cb,
(void*) &data )
!= FLAC__STREAM_DECODER_INIT_STATUS_OK )
{
err = "doing Ogg init()";
goto fail;
}
}
else
{
if (FLAC__stream_decoder_init_file( flac_dec,
pathname,
flac_write_cb,
flacMetadata,
flac_error_cb,
(void*) &data )
!= FLAC__STREAM_DECODER_INIT_STATUS_OK )
{
err = "doing init()";
goto fail;
}
}
if (!flac_process_metadata(flac_dec))
{
err = "problem reading metadata";
goto fail;
}
if (!audio_format_valid(&data.audio_format))
{
g_warning("Invalid audio format: %u:%u:%u\n",
data.audio_format.sample_rate,
data.audio_format.bits,
data.audio_format.channels);
goto fail;
}
// set track time (order is important: after stream init)
data.total_time = ((float)track_time / (float)data.audio_format.sample_rate);
data.position = 0;
decoder_initialized(decoder, &data.audio_format,
true, data.total_time);
// seek to song start (order is important: after decoder init)
flac_seek_absolute(flac_dec, (FLAC__uint64)t_start);
while (true)
{
if (!flac_process_single(flac_dec))
break;
// we only need to break at the end of track if we are in "cue mode"
if (data.time >= data.total_time)
{
flacPrintErroredState(flac_get_state(flac_dec));
flac_finish(flac_dec);
}
if (decoder_get_command(decoder) == DECODE_COMMAND_SEEK)
{
FLAC__uint64 seek_sample = t_start +
(decoder_seek_where(decoder) * data.audio_format.sample_rate);
if (seek_sample >= t_start && seek_sample <= t_end &&
flac_seek_absolute(flac_dec, (FLAC__uint64)seek_sample)) {
data.time = (float)(seek_sample - t_start) /
data.audio_format.sample_rate;
data.position = 0;
decoder_command_finished(decoder);
} else
decoder_seek_error(decoder);
}
else if (flac_get_state(flac_dec) == flac_decoder_eof)
break;
}
if (decoder_get_command(decoder) != DECODE_COMMAND_STOP)
{
flacPrintErroredState(flac_get_state(flac_dec));
flac_finish(flac_dec);
}
fail:
if (pathname)
g_free(pathname);
if (data.replay_gain_info)
replay_gain_info_free(data.replay_gain_info);
if (flac_dec)
flac_delete(flac_dec);
if (err)
g_warning("%s\n", err);
}
/**
* @brief Open a flac file for decoding
* @param const char* fname filename on fs
*/
static void
flac_filedecode_internal(struct decoder* decoder,
const char* fname,
bool is_ogg)
{
flac_decoder *flac_dec;
struct flac_data data;
const char *err = NULL;
unsigned int flac_err_state = 0;
if (!(flac_dec = flac_new()))
return;
flac_data_init(&data, decoder, NULL);
#if defined(FLAC_API_VERSION_CURRENT) && FLAC_API_VERSION_CURRENT > 7
if(!FLAC__stream_decoder_set_metadata_respond(flac_dec, FLAC__METADATA_TYPE_VORBIS_COMMENT))
{
g_debug("Failed to set metadata respond\n");
}
#endif
if (is_ogg)
{
if ( (flac_err_state = FLAC__stream_decoder_init_ogg_file( flac_dec,
fname,
flac_write_cb,
flacMetadata,
flac_error_cb,
(void*) &data ))
== FLAC__STREAM_DECODER_INIT_STATUS_ERROR_OPENING_FILE)
{
flac_container_decode(decoder, fname, is_ogg);
}
else if (flac_err_state != FLAC__STREAM_DECODER_INIT_STATUS_OK)
{
err = "doing Ogg init()";
goto fail;
}
}
else
{
if ( (flac_err_state = FLAC__stream_decoder_init_file( flac_dec,
fname,
flac_write_cb,
flacMetadata,
flac_error_cb,
(void*) &data ))
== FLAC__STREAM_DECODER_INIT_STATUS_ERROR_OPENING_FILE)
{
flac_container_decode(decoder, fname, is_ogg);
}
else if (flac_err_state != FLAC__STREAM_DECODER_INIT_STATUS_OK)
{
err = "doing init()";
goto fail;
}
}
if (!flac_process_metadata(flac_dec))
{
err = "problem reading metadata";
goto fail;
}
if (!audio_format_valid(&data.audio_format))
{
g_warning("Invalid audio format: %u:%u:%u\n",
data.audio_format.sample_rate,
data.audio_format.bits,
data.audio_format.channels);
goto fail;
}
decoder_initialized(decoder, &data.audio_format,
true, data.total_time);
while (true)
{
if (!flac_process_single(flac_dec))
break;
if (decoder_get_command(decoder) == DECODE_COMMAND_SEEK)
{
FLAC__uint64 seek_sample = decoder_seek_where(decoder) *
data.audio_format.sample_rate + 0.5;
if (flac_seek_absolute(flac_dec, seek_sample))
{
data.time = ((float)seek_sample) /
data.audio_format.sample_rate;
data.position = 0;
decoder_command_finished(decoder);
}
else
decoder_seek_error(decoder);
}
else if (flac_get_state(flac_dec) == flac_decoder_eof)
break;
}
if (decoder_get_command(decoder) != DECODE_COMMAND_STOP)
{
flacPrintErroredState(flac_get_state(flac_dec));
flac_finish(flac_dec);
}
fail:
if (data.replay_gain_info)
replay_gain_info_free(data.replay_gain_info);
if (flac_dec)
flac_delete(flac_dec);
if (err)
g_warning("%s\n", err);
}
/**
* @brief wrapper function for
* flac_filedecode_internal method
* for decoding without ogg
*/
static void
flac_filedecode(struct decoder *decoder, const char *fname)
{
struct stat st;
if (stat(fname, &st) < 0) {
flac_container_decode(decoder, fname, false);
} else
flac_filedecode_internal(decoder, fname, false);
}
#endif /* FLAC_API_VERSION_CURRENT >= 7 */
#ifndef HAVE_OGGFLAC
static bool
oggflac_init(G_GNUC_UNUSED const struct config_param *param)
{
#if defined(FLAC_API_VERSION_CURRENT) && FLAC_API_VERSION_CURRENT > 7
return !!FLAC_API_SUPPORTS_OGG_FLAC;
#else
/* disable oggflac when libflac is too old */
return false;
#endif
}
#if defined(FLAC_API_VERSION_CURRENT) && FLAC_API_VERSION_CURRENT > 7
static struct tag *
oggflac_tag_dup(const char *file)
{
struct tag *ret = NULL;
FLAC__Metadata_Iterator *it;
FLAC__StreamMetadata *block;
FLAC__Metadata_Chain *chain = FLAC__metadata_chain_new();
if (!(FLAC__metadata_chain_read_ogg(chain, file)))
goto out;
it = FLAC__metadata_iterator_new();
FLAC__metadata_iterator_init(it, chain);
ret = tag_new();
do {
if (!(block = FLAC__metadata_iterator_get_block(it)))
break;
if (block->type == FLAC__METADATA_TYPE_VORBIS_COMMENT) {
flac_vorbis_comments_to_tag(ret, NULL, block);
} else if (block->type == FLAC__METADATA_TYPE_STREAMINFO) {
ret->time = ((float)block->data.stream_info.
total_samples) /
block->data.stream_info.sample_rate + 0.5;
}
} while (FLAC__metadata_iterator_next(it));
FLAC__metadata_iterator_delete(it);
if (!tag_is_defined(ret)) {
tag_free(ret);
ret = NULL;
}
out:
FLAC__metadata_chain_delete(chain);
return ret;
}
static void
oggflac_decode(struct decoder *decoder, struct input_stream *input_stream)
{
if (ogg_stream_type_detect(input_stream) != FLAC)
return;
/* rewind the stream, because ogg_stream_type_detect() has
moved it */
input_stream_seek(input_stream, 0, SEEK_SET);
flac_decode_internal(decoder, input_stream, true);
}
static const char *const oggflac_suffixes[] = { "ogg", "oga", NULL };
static const char *const oggflac_mime_types[] = {
"application/ogg",
"application/x-ogg",
"audio/ogg",
"audio/x-flac+ogg",
"audio/x-ogg",
NULL
};
#endif /* FLAC_API_VERSION_CURRENT >= 7 */
const struct decoder_plugin oggflac_decoder_plugin = {
.name = "oggflac",
.init = oggflac_init,
#if defined(FLAC_API_VERSION_CURRENT) && FLAC_API_VERSION_CURRENT > 7
.stream_decode = oggflac_decode,
.tag_dup = oggflac_tag_dup,
.suffixes = oggflac_suffixes,
.mime_types = oggflac_mime_types
#endif
};
#endif /* HAVE_OGGFLAC */
static const char *const flac_suffixes[] = { "flac", NULL };
static const char *const flac_mime_types[] = {
"application/flac",
"application/x-flac",
"audio/flac",
"audio/x-flac",
NULL
};
const struct decoder_plugin flac_decoder_plugin = {
.name = "flac",
.stream_decode = flac_decode,
#if defined(FLAC_API_VERSION_CURRENT) && FLAC_API_VERSION_CURRENT > 7
.file_decode = flac_filedecode,
#endif /* FLAC_API_VERSION_CURRENT >= 7 */
.tag_dup = flac_tag_dup,
.suffixes = flac_suffixes,
.mime_types = flac_mime_types,
#if defined(FLAC_API_VERSION_CURRENT) && FLAC_API_VERSION_CURRENT > 7
.container_scan = flac_cue_track,
#endif /* FLAC_API_VERSION_CURRENT >= 7 */
};

Some files were not shown because too many files have changed in this diff Show More