2 * Copyright (c) 2006-2011 Ed Schouten <ed@80386.nl>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * @file audio_format_modplug.c
28 * @brief libmodplug decompression routines.
36 #include "audio_file.h"
37 #include "audio_format.h"
38 #include "audio_output.h"
41 * @brief Sample rate we want to use.
43 #define SAMPLERATE 44100
45 * @brief The number of bytes used by a single frame.
47 #define BYTESPERSAMPLE 4
50 * @brief Private data for libmodplug stored in the audio file structure.
52 struct modplug_drv_data
{
54 * @brief Base address of our memory map.
58 * @brief Length of our memory map.
62 * @brief libmodplug handle.
66 * @brief Decoded sample offset.
72 * @brief Set proper parameters in the Modplug library.
77 ModPlug_Settings mset
;
79 ModPlug_GetSettings(&mset
);
82 mset
.mFrequency
= SAMPLERATE
;
83 ModPlug_SetSettings(&mset
);
87 modplug_open(struct audio_file
*fd
, const char *ext
)
89 struct modplug_drv_data
*data
;
92 /* libmodplug doesn't have good magic. Match by extension */
95 if (g_ascii_strcasecmp(ext
, "mod") != 0 &&
96 g_ascii_strcasecmp(ext
, "s3m") != 0 &&
97 g_ascii_strcasecmp(ext
, "it") != 0 &&
98 g_ascii_strcasecmp(ext
, "xm") != 0)
101 /* If only we could memory map the internet... */
105 data
= g_slice_new(struct modplug_drv_data
);
107 /* Calculate file length */
108 fseek(fd
->fp
, 0, SEEK_END
);
109 data
->map_len
= ftello(fd
->fp
);
111 /* Memory map the entire file */
112 data
->map_base
= mmap(NULL
, data
->map_len
, PROT_READ
,
113 MAP_PRIVATE
, fileno(fd
->fp
), 0);
114 if (data
->map_base
== MAP_FAILED
)
117 /* Now feed it to modplug */
119 data
->modplug
= ModPlug_Load(data
->map_base
, data
->map_len
);
120 if (data
->modplug
!= NULL
) {
123 fd
->srate
= SAMPLERATE
;
126 fd
->time_len
= ModPlug_GetLength(data
->modplug
) / 1000;
127 title
= ModPlug_GetName(data
->modplug
);
128 if (title
!= NULL
&& title
[0] != '\0')
129 fd
->title
= g_strdup(title
);
133 munmap(data
->map_base
, data
->map_len
);
134 free
: g_slice_free(struct modplug_drv_data
, data
);
139 modplug_close(struct audio_file
*fd
)
141 struct modplug_drv_data
*data
= fd
->drv_data
;
143 ModPlug_Unload(data
->modplug
);
144 munmap(data
->map_base
, data
->map_len
);
145 g_slice_free(struct modplug_drv_data
, data
);
149 modplug_read(struct audio_file
*fd
, int16_t *buf
, size_t len
)
151 struct modplug_drv_data
*data
= fd
->drv_data
;
154 rlen
= ModPlug_Read(data
->modplug
, buf
, len
* sizeof(int16_t));
155 data
->sample
+= rlen
/ BYTESPERSAMPLE
;
156 fd
->time_cur
= data
->sample
/ SAMPLERATE
;
158 return (rlen
/ sizeof(int16_t));
162 modplug_seek(struct audio_file
*fd
, int len
, int rel
)
164 struct modplug_drv_data
*data
= fd
->drv_data
;
167 off
= len
* SAMPLERATE
;
170 /* Don't seek out of reach */
171 off
= CLAMP(off
, 0, (fd
->time_len
- 1) * SAMPLERATE
);
173 ModPlug_Seek(data
->modplug
, (off
* 10) / (SAMPLERATE
/ 100));
175 fd
->time_cur
= data
->sample
/ SAMPLERATE
;