Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
M
mpd
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Иван Мажукин
mpd
Commits
4e909f94
Commit
4e909f94
authored
Oct 03, 2011
by
Max Kellermann
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
decoder/dsdiff: new decoder plugin
Doesn't seem to work yet, getting just noise from a test file. Seeking isn't implemented yet.
parent
99f49e26
Show whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
516 additions
and
0 deletions
+516
-0
Makefile.am
Makefile.am
+3
-0
NEWS
NEWS
+1
-0
dsdiff_decoder_plugin.c
src/decoder/dsdiff_decoder_plugin.c
+485
-0
dsdiff_decoder_plugin.h
src/decoder/dsdiff_decoder_plugin.h
+25
-0
decoder_list.c
src/decoder_list.c
+2
-0
No files found.
Makefile.am
View file @
4e909f94
...
@@ -490,6 +490,9 @@ DECODER_LIBS = \
...
@@ -490,6 +490,9 @@ DECODER_LIBS = \
DECODER_SRC
=
\
DECODER_SRC
=
\
src/decoder/pcm_decoder_plugin.c
\
src/decoder/pcm_decoder_plugin.c
\
src/decoder/dsdiff_decoder_plugin.c
\
src/decoder/dsdiff_decoder_plugin.h
\
src/dsd2pcm/dsd2pcm.c src/dsd2pcm/dsd2pcm.h
\
src/decoder_buffer.c
\
src/decoder_buffer.c
\
src/decoder_plugin.c
\
src/decoder_plugin.c
\
src/decoder_list.c
src/decoder_list.c
...
...
NEWS
View file @
4e909f94
...
@@ -13,6 +13,7 @@ ver 0.17 (2011/??/??)
...
@@ -13,6 +13,7 @@ ver 0.17 (2011/??/??)
- ffmpeg: drop support for pre-0.5 ffmpeg
- ffmpeg: drop support for pre-0.5 ffmpeg
- ffmpeg: support libavformat 0.7
- ffmpeg: support libavformat 0.7
- oggflac: delete this obsolete plugin
- oggflac: delete this obsolete plugin
- dsdiff: new decoder plugin
* output:
* output:
- httpd: support for streaming to a DLNA client
- httpd: support for streaming to a DLNA client
- osx: allow user to specify other audio devices
- osx: allow user to specify other audio devices
...
...
src/decoder/dsdiff_decoder_plugin.c
0 → 100644
View file @
4e909f94
/*
* Copyright (C) 2003-2011 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 "dsdiff_decoder_plugin.h"
#include "decoder_api.h"
#include "audio_check.h"
#include "dsd2pcm/dsd2pcm.h"
#include <unistd.h>
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "dsdiff"
struct
dsdiff_id
{
char
value
[
4
];
};
struct
dsdiff_header
{
struct
dsdiff_id
id
;
uint32_t
size_high
,
size_low
;
struct
dsdiff_id
format
;
};
struct
dsdiff_chunk_header
{
struct
dsdiff_id
id
;
uint32_t
size_high
,
size_low
;
};
struct
dsdiff_metadata
{
unsigned
sample_rate
,
channels
;
};
static
bool
dsdiff_id_equals
(
const
struct
dsdiff_id
*
id
,
const
char
*
s
)
{
assert
(
id
!=
NULL
);
assert
(
s
!=
NULL
);
assert
(
strlen
(
s
)
==
sizeof
(
id
->
value
));
return
memcmp
(
id
->
value
,
s
,
sizeof
(
id
->
value
))
==
0
;
}
/**
* Read the "size" attribute from the specified header, converting it
* to the host byte order if needed.
*/
G_GNUC_CONST
static
uint64_t
dsdiff_chunk_size
(
const
struct
dsdiff_chunk_header
*
header
)
{
return
(((
uint64_t
)
GUINT32_FROM_BE
(
header
->
size_high
))
<<
32
)
|
((
uint64_t
)
GUINT32_FROM_BE
(
header
->
size_low
));
}
static
bool
dsdiff_read
(
struct
decoder
*
decoder
,
struct
input_stream
*
is
,
void
*
data
,
size_t
length
)
{
size_t
nbytes
=
decoder_read
(
decoder
,
is
,
data
,
length
);
return
nbytes
==
length
;
}
static
bool
dsdiff_read_id
(
struct
decoder
*
decoder
,
struct
input_stream
*
is
,
struct
dsdiff_id
*
id
)
{
return
dsdiff_read
(
decoder
,
is
,
id
,
sizeof
(
*
id
));
}
static
bool
dsdiff_read_chunk_header
(
struct
decoder
*
decoder
,
struct
input_stream
*
is
,
struct
dsdiff_chunk_header
*
header
)
{
return
dsdiff_read
(
decoder
,
is
,
header
,
sizeof
(
*
header
));
}
static
bool
dsdiff_read_payload
(
struct
decoder
*
decoder
,
struct
input_stream
*
is
,
const
struct
dsdiff_chunk_header
*
header
,
void
*
data
,
size_t
length
)
{
uint64_t
size
=
dsdiff_chunk_size
(
header
);
if
(
size
!=
(
uint64_t
)
length
)
return
false
;
size_t
nbytes
=
decoder_read
(
decoder
,
is
,
data
,
length
);
return
nbytes
==
length
;
}
/**
* Skip the #input_stream to the specified offset.
*/
static
bool
dsdiff_skip_to
(
struct
decoder
*
decoder
,
struct
input_stream
*
is
,
goffset
offset
)
{
if
(
is
->
seekable
)
return
input_stream_seek
(
is
,
offset
,
SEEK_SET
,
NULL
);
if
(
is
->
offset
>
offset
)
return
false
;
char
buffer
[
8192
];
while
(
is
->
offset
<
offset
)
{
size_t
length
=
sizeof
(
buffer
);
if
(
offset
-
is
->
offset
<
(
goffset
)
length
)
length
=
offset
-
is
->
offset
;
size_t
nbytes
=
decoder_read
(
decoder
,
is
,
buffer
,
length
);
if
(
nbytes
==
0
)
return
false
;
}
assert
(
is
->
offset
==
offset
);
return
true
;
}
/**
* Skip some bytes from the #input_stream.
*/
static
bool
dsdiff_skip
(
struct
decoder
*
decoder
,
struct
input_stream
*
is
,
goffset
delta
)
{
assert
(
delta
>=
0
);
if
(
delta
==
0
)
return
true
;
if
(
is
->
seekable
)
return
input_stream_seek
(
is
,
delta
,
SEEK_CUR
,
NULL
);
char
buffer
[
8192
];
while
(
delta
>
0
)
{
size_t
length
=
sizeof
(
buffer
);
if
((
goffset
)
length
>
delta
)
length
=
delta
;
size_t
nbytes
=
decoder_read
(
decoder
,
is
,
buffer
,
length
);
if
(
nbytes
==
0
)
return
false
;
delta
-=
nbytes
;
}
return
true
;
}
/**
* Read and parse a "SND" chunk inside "PROP".
*/
static
bool
dsdiff_read_prop_snd
(
struct
decoder
*
decoder
,
struct
input_stream
*
is
,
struct
dsdiff_metadata
*
metadata
,
goffset
end_offset
)
{
struct
dsdiff_chunk_header
header
;
while
((
goffset
)(
is
->
offset
+
sizeof
(
header
))
<=
end_offset
)
{
if
(
!
dsdiff_read_chunk_header
(
decoder
,
is
,
&
header
))
return
false
;
goffset
chunk_end_offset
=
is
->
offset
+
dsdiff_chunk_size
(
&
header
);
if
(
chunk_end_offset
>
end_offset
)
return
false
;
if
(
dsdiff_id_equals
(
&
header
.
id
,
"FS "
))
{
uint32_t
sample_rate
;
if
(
!
dsdiff_read_payload
(
decoder
,
is
,
&
header
,
&
sample_rate
,
sizeof
(
sample_rate
)))
return
false
;
metadata
->
sample_rate
=
GUINT32_FROM_BE
(
sample_rate
);
}
else
if
(
dsdiff_id_equals
(
&
header
.
id
,
"CHNL"
))
{
uint16_t
channels
;
if
(
dsdiff_chunk_size
(
&
header
)
<
sizeof
(
channels
)
||
!
dsdiff_read
(
decoder
,
is
,
&
channels
,
sizeof
(
channels
))
||
!
dsdiff_skip_to
(
decoder
,
is
,
chunk_end_offset
))
return
false
;
metadata
->
channels
=
GUINT16_FROM_BE
(
channels
);
}
else
if
(
dsdiff_id_equals
(
&
header
.
id
,
"CMPR"
))
{
struct
dsdiff_id
type
;
if
(
dsdiff_chunk_size
(
&
header
)
<
sizeof
(
type
)
||
!
dsdiff_read
(
decoder
,
is
,
&
type
,
sizeof
(
type
))
||
!
dsdiff_skip_to
(
decoder
,
is
,
chunk_end_offset
))
return
false
;
if
(
!
dsdiff_id_equals
(
&
type
,
"DSD "
))
/* only uincompressed DSD audio data
is implemented */
return
false
;
}
else
{
/* ignore unknown chunk */
if
(
!
dsdiff_skip_to
(
decoder
,
is
,
chunk_end_offset
))
return
false
;
}
}
return
is
->
offset
==
end_offset
;
}
/**
* Read and parse a "PROP" chunk.
*/
static
bool
dsdiff_read_prop
(
struct
decoder
*
decoder
,
struct
input_stream
*
is
,
struct
dsdiff_metadata
*
metadata
,
const
struct
dsdiff_chunk_header
*
prop_header
)
{
uint64_t
prop_size
=
dsdiff_chunk_size
(
prop_header
);
goffset
end_offset
=
is
->
offset
+
prop_size
;
struct
dsdiff_id
prop_id
;
if
(
prop_size
<
sizeof
(
prop_id
)
||
!
dsdiff_read_id
(
decoder
,
is
,
&
prop_id
))
return
false
;
if
(
dsdiff_id_equals
(
&
prop_id
,
"SND "
))
return
dsdiff_read_prop_snd
(
decoder
,
is
,
metadata
,
end_offset
);
else
/* ignore unknown PROP chunk */
return
dsdiff_skip_to
(
decoder
,
is
,
end_offset
);
}
/**
* Read and parse all metadata chunks at the beginning. Stop when the
* first "DSD" chunk is seen, and return its header in the
* "chunk_header" parameter.
*/
static
bool
dsdiff_read_metadata
(
struct
decoder
*
decoder
,
struct
input_stream
*
is
,
struct
dsdiff_metadata
*
metadata
,
struct
dsdiff_chunk_header
*
chunk_header
)
{
struct
dsdiff_header
header
;
if
(
!
dsdiff_read
(
decoder
,
is
,
&
header
,
sizeof
(
header
))
||
!
dsdiff_id_equals
(
&
header
.
id
,
"FRM8"
)
||
!
dsdiff_id_equals
(
&
header
.
format
,
"DSD "
))
return
false
;
while
(
true
)
{
if
(
!
dsdiff_read_chunk_header
(
decoder
,
is
,
chunk_header
))
return
false
;
if
(
dsdiff_id_equals
(
&
chunk_header
->
id
,
"PROP"
))
{
if
(
!
dsdiff_read_prop
(
decoder
,
is
,
metadata
,
chunk_header
))
return
false
;
}
else
if
(
dsdiff_id_equals
(
&
chunk_header
->
id
,
"DSD "
))
{
/* done with metadata */
return
true
;
}
else
{
/* ignore unknown chunk */
uint64_t
chunk_size
=
dsdiff_chunk_size
(
chunk_header
);
goffset
chunk_end_offset
=
is
->
offset
+
chunk_size
;
if
(
!
dsdiff_skip_to
(
decoder
,
is
,
chunk_end_offset
))
return
false
;
}
}
}
G_GNUC_CONST
static
inline
int32_t
clip
(
int32_t
min
,
int32_t
value
,
int32_t
max
)
{
if
(
G_UNLIKELY
(
value
<
min
))
value
=
min
;
else
if
(
G_UNLIKELY
(
value
>
max
))
value
=
max
;
return
value
;
}
/**
* Convert an array of float samples [-1 .. +1] to padded 24 bit
* samples.
*/
static
void
float_to_uint24
(
int32_t
*
dest
,
const
float
*
src
,
size_t
length
)
{
const
float
*
src_end
=
src
+
length
;
while
(
src
<
src_end
)
{
float
f_sample
=
*
src
++
*
8388608
;
*
dest
++
=
clip
(
-
8388608
,
(
int32_t
)
f_sample
,
8388607
);
}
}
/**
* Decode one "DSD" chunk.
*/
static
bool
dsdiff_decode_chunk
(
struct
decoder
*
decoder
,
struct
input_stream
*
is
,
unsigned
channels
,
dsd2pcm_ctx
**
dsd2pcm
,
uint64_t
chunk_size
)
{
uint8_t
buffer
[
8192
];
const
size_t
sample_size
=
sizeof
(
buffer
[
0
]);
const
size_t
frame_size
=
channels
*
sample_size
;
const
unsigned
buffer_frames
=
sizeof
(
buffer
)
/
frame_size
;
const
unsigned
buffer_samples
=
buffer_frames
*
frame_size
;
const
size_t
buffer_size
=
buffer_samples
*
sample_size
;
float
f_buffer
[
G_N_ELEMENTS
(
buffer
)];
int32_t
i_buffer
[
G_N_ELEMENTS
(
buffer
)];
while
(
chunk_size
>
0
)
{
/* see how much aligned data from the remaining chunk
fits into the local buffer */
unsigned
now_frames
=
buffer_frames
;
size_t
now_size
=
buffer_size
;
unsigned
now_samples
=
buffer_samples
;
if
(
chunk_size
<
(
uint64_t
)
now_size
)
{
now_frames
=
(
unsigned
)
chunk_size
/
frame_size
;
now_size
=
now_frames
*
frame_size
;
now_samples
=
now_frames
*
channels
;
}
size_t
nbytes
=
decoder_read
(
decoder
,
is
,
buffer
,
now_size
);
if
(
nbytes
!=
now_size
)
return
false
;
chunk_size
-=
nbytes
;
/* invoke the dsp2pcm library, once for each
channel */
for
(
unsigned
c
=
0
;
c
<
channels
;
++
c
)
dsd2pcm_translate
(
dsd2pcm
[
c
],
now_frames
,
buffer
+
c
,
channels
,
true
,
f_buffer
+
c
,
channels
);
/* convert to integer and submit to the decoder API */
float_to_uint24
(
i_buffer
,
f_buffer
,
now_samples
);
enum
decoder_command
cmd
=
decoder_data
(
decoder
,
is
,
i_buffer
,
now_samples
*
sizeof
(
i_buffer
[
0
]),
0
);
switch
(
cmd
)
{
case
DECODE_COMMAND_NONE
:
break
;
case
DECODE_COMMAND_START
:
case
DECODE_COMMAND_STOP
:
return
false
;
case
DECODE_COMMAND_SEEK
:
/* not implemented yet */
decoder_seek_error
(
decoder
);
break
;
}
}
return
dsdiff_skip
(
decoder
,
is
,
chunk_size
);
}
static
void
dsdiff_stream_decode
(
struct
decoder
*
decoder
,
struct
input_stream
*
is
)
{
struct
dsdiff_metadata
metadata
=
{
.
sample_rate
=
0
,
.
channels
=
0
,
};
struct
dsdiff_chunk_header
chunk_header
;
if
(
!
dsdiff_read_metadata
(
decoder
,
is
,
&
metadata
,
&
chunk_header
))
return
;
GError
*
error
=
NULL
;
struct
audio_format
audio_format
;
if
(
!
audio_format_init_checked
(
&
audio_format
,
metadata
.
sample_rate
/
8
,
SAMPLE_FORMAT_S24_P32
,
metadata
.
channels
,
&
error
))
{
g_warning
(
"%s"
,
error
->
message
);
g_error_free
(
error
);
return
;
}
/* initialize the dsd2pcm library */
dsd2pcm_ctx
*
dsd2pcm
[
MAX_CHANNELS
];
for
(
unsigned
i
=
0
;
i
<
metadata
.
channels
;
++
i
)
{
dsd2pcm
[
i
]
=
dsd2pcm_init
();
if
(
dsd2pcm
[
i
]
==
NULL
)
{
for
(
unsigned
j
=
0
;
j
<
i
;
++
j
)
dsd2pcm_destroy
(
dsd2pcm
[
j
]);
return
;
}
}
/* success: file was recognized */
decoder_initialized
(
decoder
,
&
audio_format
,
false
,
-
1
);
/* every iteration of the following loop decodes one "DSD"
chunk */
while
(
true
)
{
uint64_t
chunk_size
=
dsdiff_chunk_size
(
&
chunk_header
);
if
(
dsdiff_id_equals
(
&
chunk_header
.
id
,
"DSD "
))
{
if
(
!
dsdiff_decode_chunk
(
decoder
,
is
,
metadata
.
channels
,
dsd2pcm
,
chunk_size
))
break
;
}
else
{
/* ignore other chunks */
if
(
!
dsdiff_skip
(
decoder
,
is
,
chunk_size
))
break
;
}
/* read next chunk header; the first one was read by
dsdiff_read_metadata() */
if
(
!
dsdiff_read_chunk_header
(
decoder
,
is
,
&
chunk_header
))
break
;
}
for
(
unsigned
i
=
0
;
i
<
metadata
.
channels
;
++
i
)
dsd2pcm_destroy
(
dsd2pcm
[
i
]);
}
static
struct
tag
*
dsdiff_stream_tag
(
struct
input_stream
*
is
)
{
struct
dsdiff_metadata
metadata
=
{
.
sample_rate
=
0
,
.
channels
=
0
,
};
struct
dsdiff_chunk_header
chunk_header
;
if
(
!
dsdiff_read_metadata
(
NULL
,
is
,
&
metadata
,
&
chunk_header
))
return
NULL
;
struct
audio_format
audio_format
;
if
(
!
audio_format_init_checked
(
&
audio_format
,
metadata
.
sample_rate
/
8
,
SAMPLE_FORMAT_S24_P32
,
metadata
.
channels
,
NULL
))
/* refuse to parse files which we cannot play anyway */
return
NULL
;
/* no total time estimate, no tags implemented yet */
return
tag_new
();
}
static
const
char
*
const
dsdiff_suffixes
[]
=
{
"dff"
,
NULL
};
static
const
char
*
const
dsdiff_mime_types
[]
=
{
"application/x-dff"
,
NULL
};
const
struct
decoder_plugin
dsdiff_decoder_plugin
=
{
.
name
=
"dsdiff"
,
.
stream_decode
=
dsdiff_stream_decode
,
.
stream_tag
=
dsdiff_stream_tag
,
.
suffixes
=
dsdiff_suffixes
,
.
mime_types
=
dsdiff_mime_types
,
};
src/decoder/dsdiff_decoder_plugin.h
0 → 100644
View file @
4e909f94
/*
* Copyright (C) 2003-2011 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_DECODER_DSDIFF_H
#define MPD_DECODER_DSDIFF_H
extern
const
struct
decoder_plugin
dsdiff_decoder_plugin
;
#endif
src/decoder_list.c
View file @
4e909f94
...
@@ -24,6 +24,7 @@
...
@@ -24,6 +24,7 @@
#include "conf.h"
#include "conf.h"
#include "mpd_error.h"
#include "mpd_error.h"
#include "decoder/pcm_decoder_plugin.h"
#include "decoder/pcm_decoder_plugin.h"
#include "decoder/dsdiff_decoder_plugin.h"
#include <glib.h>
#include <glib.h>
...
@@ -70,6 +71,7 @@ const struct decoder_plugin *const decoder_plugins[] = {
...
@@ -70,6 +71,7 @@ const struct decoder_plugin *const decoder_plugins[] = {
#ifdef HAVE_AUDIOFILE
#ifdef HAVE_AUDIOFILE
&
audiofile_decoder_plugin
,
&
audiofile_decoder_plugin
,
#endif
#endif
&
dsdiff_decoder_plugin
,
#ifdef HAVE_FAAD
#ifdef HAVE_FAAD
&
faad_decoder_plugin
,
&
faad_decoder_plugin
,
#endif
#endif
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment