diff options
author | Amaury Pouly <amaury.pouly@gmail.com> | 2017-02-23 11:33:19 +0100 |
---|---|---|
committer | Amaury Pouly <amaury.pouly@gmail.com> | 2017-09-05 21:42:12 +0200 |
commit | 1d121e8c082fe67757cf0d4df7b9e6ca1e26f755 (patch) | |
tree | 1c93842d99bb8e4a5f3ed5bca38c05e7f17894fa /firmware/target/hosted/pcm-alsa.c | |
parent | 142f80f07d96305f1618c99c28c13319b1b279e6 (diff) |
Initial commit for the Sony NWZ linux port
SUPPORTED SERIES:
- NWZ-E450
- NWZ-E460
- NWZ-E470
- NWZ-E580
- NWZ-A10
NOTES:
- bootloader makefile convert an extra font to be installed alongside the bootloader
since sysfont is way too small
- the toolsicon bitmap comes from the Oxygen iconset
- touchscreen driver is untested
TODO:
- implement audio routing driver (pcm is handled by pcm-alsa)
- fix playback: it crashes on illegal instruction in DEBUG builds
- find out why the browser starts at / instead of /contents
- implement radio support
- implement return to OF for usb handling
- calibrate battery curve (NB: of can report a battery level on a 0-5 scale but
probabl don't want to use that ?)
- implement simulator build (we need a nice image of the player)
- figure out if we can detect jack removal
POTENTIAL TODOS:
- try to build a usb serial gadget and gdbserver
Change-Id: Ic77d71e0651355d47cc4e423a40fb64a60c69a80
Diffstat (limited to 'firmware/target/hosted/pcm-alsa.c')
-rw-r--r-- | firmware/target/hosted/pcm-alsa.c | 89 |
1 files changed, 72 insertions, 17 deletions
diff --git a/firmware/target/hosted/pcm-alsa.c b/firmware/target/hosted/pcm-alsa.c index 715af58fd9..d55eebccc1 100644 --- a/firmware/target/hosted/pcm-alsa.c +++ b/firmware/target/hosted/pcm-alsa.c @@ -50,12 +50,14 @@ #include "system.h" #include "debug.h" #include "kernel.h" +#include "panic.h" #include "pcm.h" #include "pcm-internal.h" #include "pcm_mixer.h" #include "pcm_sampr.h" #include "audiohw.h" +#include "pcm-alsa.h" #include <pthread.h> #include <signal.h> @@ -66,14 +68,21 @@ * with multple applications running */ static char device[] = "plughw:0,0"; /* playback device */ static const snd_pcm_access_t access_ = SND_PCM_ACCESS_RW_INTERLEAVED; /* access mode */ +#ifdef SONY_NWZ_LINUX +/* Sony NWZ must use 32-bit per sample */ +static const snd_pcm_format_t format = SND_PCM_FORMAT_S32_LE; /* sample format */ +typedef long sample_t; +#else static const snd_pcm_format_t format = SND_PCM_FORMAT_S16; /* sample format */ +typedef short sample_t; +#endif static const int channels = 2; /* count of channels */ static unsigned int rate = 44100; /* stream rate */ static snd_pcm_t *handle; static snd_pcm_sframes_t buffer_size = MIX_FRAME_SAMPLES * 32; /* ~16k */ static snd_pcm_sframes_t period_size = MIX_FRAME_SAMPLES * 4; /* ~4k */ -static short *frames; +static sample_t *frames; static const void *pcm_data = 0; static size_t pcm_size = 0; @@ -153,7 +162,7 @@ static int set_hwparams(snd_pcm_t *handle, unsigned sample_rate) goto error; } if (!frames) - frames = malloc(period_size * channels * sizeof(short)); + frames = malloc(period_size * channels * sizeof(sample_t)); /* write the parameters to device */ err = snd_pcm_hw_params(handle, params); @@ -212,6 +221,40 @@ error: return err; } +#ifdef SONY_NWZ_LINUX +/* Digital volume explanation: + * with very good approximation (<0.1dB) the convertion from dB to multiplicative + * factor, for dB>=0, is 2^(dB/3). We can then notice that if we write dB=3*k+r + * then this is 2^k*2^(r/3) so we only need to look at r=0,1,2. For r=0 this is + * 1, for r=1 we have 2^(1/3)~=1.25 so we approximate by 1+1/4, and 2^(2/3)~=1.5 + * so we approximate by 1+1/2. To go from negative to nonnegative we notice that + * 48 dB => 63095 factor ~= 2^16 so we virtually pre-multiply everything by 2^(-16) + * and add 48dB to the input volume. We cannot go lower -43dB because several + * values between -48dB and -43dB would require a fractional multiplier, which is + * stupid to implement for such very low volume. */ +static int dig_vol_mult = 2 ^ 16; /* multiplicative factor to apply to each sample */ + +void pcm_alsa_set_digital_volume(int vol_db) +{ + if(vol_db > 0 || vol_db < -43) + panicf("invalid pcm alsa volume"); + if(format != SND_PCM_FORMAT_S32_LE) + panicf("this function assumes 32-bit sample size"); + vol_db += 48; /* -42dB .. 0dB => 5dB .. 48dB */ + /* NOTE if vol_dB = 5 then vol_shift = 1 but r = 1 so we do vol_shift - 1 >= 0 + * otherwise vol_dB >= 0 implies vol_shift >= 2 so vol_shift - 2 >= 0 */ + int vol_shift = vol_db / 3; + int r = vol_db % 3; + if(r == 0) + dig_vol_mult = 1 << vol_shift; + else if(r == 1) + dig_vol_mult = 1 << vol_shift | 1 << (vol_shift - 2); + else + dig_vol_mult = 1 << vol_shift | 1 << (vol_shift - 1); + printf("%d dB -> factor = %d\n", vol_db - 48, dig_vol_mult); +} +#endif + /* copy pcm samples to a spare buffer, suitable for snd_pcm_writei() */ static bool fill_frames(void) { @@ -229,12 +272,28 @@ static bool fill_frames(void) return false; } } - copy_n = MIN((ssize_t)pcm_size, frames_left*4); - memcpy(&frames[2*(period_size-frames_left)], pcm_data, copy_n); - pcm_data += copy_n; - pcm_size -= copy_n; - frames_left -= copy_n/4; + if (pcm_size % 4) + panicf("Wrong pcm_size"); + /* the compiler will optimize this test away */ + copy_n = MIN((ssize_t)pcm_size/4, frames_left); + if (format == SND_PCM_FORMAT_S32_LE) + { + /* We have to convert 16-bit to 32-bit, the need to multiply the + * sample by some value so the sound is not too low */ + const short *pcm_ptr = pcm_data; + sample_t *sample_ptr = &frames[2*(period_size-frames_left)]; + for (int i = 0; i < copy_n*2; i++) + *sample_ptr++ = *pcm_ptr++ * dig_vol_mult; + } + else + { + /* Rockbox and PCM have same format: memcopy */ + memcpy(&frames[2*(period_size-frames_left)], pcm_data, copy_n); + } + pcm_data += copy_n*4; + pcm_size -= copy_n*4; + frames_left -= copy_n; if (new_buffer) { @@ -285,7 +344,7 @@ static int async_rw(snd_pcm_t *handle) { int err; snd_pcm_sframes_t sample_size; - short *samples; + sample_t *samples; #ifdef USE_ASYNC_CALLBACK /* assign alternative stack for the signal handlers */ @@ -323,7 +382,7 @@ static int async_rw(snd_pcm_t *handle) /* fill buffer with silence to initiate playback without noisy click */ sample_size = buffer_size; - samples = malloc(sample_size * channels * sizeof(short)); + samples = malloc(sample_size * channels * sizeof(sample_t)); snd_pcm_format_set_silence(format, samples, sample_size); err = snd_pcm_writei(handle, samples, sample_size); @@ -367,23 +426,19 @@ void pcm_play_dma_init(void) if ((err = snd_pcm_open(&handle, device, SND_PCM_STREAM_PLAYBACK, 0)) < 0) { - printf("%s(): Cannot open device %s: %s\n", __func__, device, snd_strerror(err)); - exit(EXIT_FAILURE); - return; + panicf("%s(): Cannot open device %s: %s\n", __func__, device, snd_strerror(err)); } if ((err = snd_pcm_nonblock(handle, 1))) - printf("Could not set non-block mode: %s\n", snd_strerror(err)); + panicf("Could not set non-block mode: %s\n", snd_strerror(err)); if ((err = set_hwparams(handle, rate)) < 0) { - printf("Setting of hwparams failed: %s\n", snd_strerror(err)); - exit(EXIT_FAILURE); + panicf("Setting of hwparams failed: %s\n", snd_strerror(err)); } if ((err = set_swparams(handle)) < 0) { - printf("Setting of swparams failed: %s\n", snd_strerror(err)); - exit(EXIT_FAILURE); + panicf("Setting of swparams failed: %s\n", snd_strerror(err)); } pcm_dma_apply_settings(); |