Python: Experimental audio API

This commit is contained in:
Vicki Pfau 2018-10-07 15:21:52 -07:00
parent b790dd8e75
commit 4346c5db1b
5 changed files with 77 additions and 1 deletions

View File

@ -111,6 +111,7 @@ Features:
- Qt: State file load/save menu options - Qt: State file load/save menu options
- Windows installer - Windows installer
- Tile viewer now has adjustable width - Tile viewer now has adjustable width
- Python: Experimental audio API
Bugfixes: Bugfixes:
- PSP2: Fix audio crackling after fast forward - PSP2: Fix audio crackling after fast forward
- PSP2: Fix audio crackling when buffer is full - PSP2: Fix audio crackling when buffer is full

View File

@ -35,6 +35,7 @@ void free(void*);
#include "flags.h" #include "flags.h"
#include <mgba/core/blip_buf.h>
#include <mgba/core/cache-set.h> #include <mgba/core/cache-set.h>
#include <mgba/core/core.h> #include <mgba/core/core.h>
#include <mgba/core/map-cache.h> #include <mgba/core/map-cache.h>

View File

@ -20,6 +20,7 @@ ffi.set_source("mgba._pylib", """
#define inline #define inline
#include "flags.h" #include "flags.h"
#define OPAQUE_THREADING #define OPAQUE_THREADING
#include <mgba/core/blip_buf.h>
#include <mgba/core/cache-set.h> #include <mgba/core/cache-set.h>
#include <mgba-util/common.h> #include <mgba-util/common.h>
#include <mgba/core/core.h> #include <mgba/core/core.h>

View File

@ -0,0 +1,57 @@
# Copyright (c) 2013-2018 Jeffrey Pfau
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
from ._pylib import ffi, lib # pylint: disable=no-name-in-module
class Buffer(object):
def __init__(self, native, internal_rate):
self._native = native
self._internal_rate = internal_rate
@property
def available(self):
return lib.blip_samples_avail(self._native)
def set_rate(self, rate):
lib.blip_set_rates(self._native, self._internal_rate, rate)
def read(self, samples):
buffer = ffi.new("short[%i]" % samples)
count = self.read_into(buffer, samples, 1, 0)
return buffer[:count]
def read_into(self, buffer, samples, channels=1, interleave=0):
return lib.blip_read_samples(self._native, ffi.addressof(buffer, interleave), samples, channels == 2)
def clear(self):
lib.blip_clear(self._native)
class StereoBuffer(object):
def __init__(self, left, right):
self._left = left
self._right = right
@property
def available(self):
return min(self._left.available, self._right.available)
def set_rate(self, rate):
self._left.set_rate(rate)
self._right.set_rate(rate)
def read(self, samples):
buffer = ffi.new("short[%i]" % (2 * samples))
count = self.read_into(buffer, samples)
return buffer[0:2 * count]
def read_into(self, buffer, samples):
samples = self._left.read_into(buffer, samples, 2, 0)
return self._right.read_into(buffer, samples, 2, 1)
def clear(self):
self._left.clear()
self._right.clear()

View File

@ -4,7 +4,7 @@
# License, v. 2.0. If a copy of the MPL was not distributed with this # License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/. # file, You can obtain one at http://mozilla.org/MPL/2.0/.
from ._pylib import ffi, lib # pylint: disable=no-name-in-module from ._pylib import ffi, lib # pylint: disable=no-name-in-module
from . import tile from . import tile, audio
from cached_property import cached_property from cached_property import cached_property
from functools import wraps from functools import wraps
@ -225,6 +225,22 @@ class Core(object):
def set_video_buffer(self, image): def set_video_buffer(self, image):
self._core.setVideoBuffer(self._core, image.buffer, image.stride) self._core.setVideoBuffer(self._core, image.buffer, image.stride)
@protected
def set_audio_buffer_size(self, size)
self._core.setAudioBufferSize(self._core, size)
@property
def audio_buffer_size(self):
return self._core.getAudioBufferSize(self._core)
@protected
def get_audio_channels(self):
return audio.StereoBuffer(self.get_audio_channel(0), self.get_audio_channel(1));
@protected
def get_audio_channel(self, channel):
return audio.Buffer(self._core.getAudioChannel(self._core, channel), self.frequency)
@protected @protected
def reset(self): def reset(self):
self._core.reset(self._core) self._core.reset(self._core)