Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

1.0.21 #22

Merged
merged 7 commits into from
Jan 17, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

<img alt="logo" src="https://github.com/umjammer/vavi-sound/assets/493908/7a731744-643a-4b6c-b82b-68f2fcc436c9" width="160" />

Provides old school Japanese cell phone sounds library as `javax.sound` SPI<br/>
Provides old school Japanese cell phone sounds library as `javax.sound(.midi)` SPI<br/>
includes many ADPCM codecs and the [SSRC](https://github.com/shibatch/SSRC) sampling rate converter.

### Status
Expand Down Expand Up @@ -38,6 +38,11 @@ includes many ADPCM codecs and the [SSRC](https://github.com/shibatch/SSRC) samp

## Usage

### sample

* MFi (.mld) ... [PlayMFi](src/test/java/PlayMFi.java)
* SMAF (.mmf) ... [PlaySMAF](src/test/java/PlaySMAF.java)

### FAQ

#### Q. can I use SSRC sampling converter under LGPL license?
Expand All @@ -57,7 +62,8 @@ A. yes you can, follow those steps

### Tech Know

* github actions workflow on ubuntu java8 cannot deal line `PCM_SIGNED 8000.0 Hz, 16 bit, mono, 2 bytes/frame, little-endian`
* \[github actions] workflow on ubuntu java8 cannot deal line `PCM_SIGNED 8000.0 Hz, 16 bit, mono, 2 bytes/frame, little-endian`
* \[midi volume] avoiding noise, `SoundUtil#volume` should be called before `Sequencer#setSequence`

## References

Expand All @@ -74,6 +80,7 @@ A. yes you can, follow those steps
* service loader instead of vavi.properties
* [RTTTL (Ringing Tones text transfer language)](https://web.archive.org/web/20070704033948/http://www.convertyourtone.com/rtttl.html)
* https://github.com/SatyrDiamond/adpcm
* midi -> smaf

---
<sub>images by <a href="https://www.silhouette-illust.com/illust/49312">melody</a>, <a href="https://www.silhouette-illust.com/illust/257">cellphone</a></sub>
10 changes: 5 additions & 5 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,17 @@

<groupId>vavi</groupId>
<artifactId>vavi-sound</artifactId>
<version>1.0.20</version>
<version>1.0.21</version>

<name>Vavi Sound API</name>
<name>Vavi Sound SPIs</name>
<url>https://github.com/umjammer/vavi-sound</url>
<scm>
<url>https://github.com/umjammer/vavi-sound</url>
</scm>
<description>
TODO

adpcm spi</description>
Provides old school Japanese cell phone sounds library as javax.sound SPI
includes many ADPCM codecs and the SSRC sampling rate converter.
</description>
<issueManagement>
<url>https://github.com/umjammer/vavi-sound/issues</url>
</issueManagement>
Expand Down
20 changes: 10 additions & 10 deletions src/main/java/vavi/sound/adpcm/ima/Ima.java
Original file line number Diff line number Diff line change
Expand Up @@ -153,13 +153,13 @@ private static void decode(int channel,
if (c != cm) {
val -= dp;
if (val < -0x8000) {
//logger.log(Level.TRACE, String.format("%04x -> %d", val, -0x8000));
//logger.log(Level.TRACE, "%04x -> %d".formatted(val, -0x8000));
val = -0x8000;
}
} else {
val += dp;
if (val > 0x7fff) {
//logger.log(Level.TRACE, String.format("%04x -> %d", val, 0x7fff));
//logger.log(Level.TRACE, "%04x -> %d".formatted(val, 0x7fff));
val = 0x7fff;
}
}
Expand Down Expand Up @@ -318,14 +318,14 @@ private static int encode(int ch, int chans, int v0, int[] ibuff, int n, int[] s
* @param outBuffer output buffer[blockAlign]
* @param option non-zero allows some cpu-intensive code to improve output
*/
private void encodeChannel(int channel,
int channels,
int[] inBuffer,
int length,
int[] steps,
int sp,
byte[] outBuffer,
int option) {
private static void encodeChannel(int channel,
int channels,
int[] inBuffer,
int length,
int[] steps,
int sp,
byte[] outBuffer,
int option) {

int[] snext = new int[1];
int d;
Expand Down
8 changes: 4 additions & 4 deletions src/main/java/vavi/sound/adpcm/ms/Ms.java
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,7 @@ private static int encode(int channel,
// FIXME does c << 0 work properly ?
outBuffer[op + (ox >> 3)] |= (byte) ((ox & 4) != 0 ? c : (c << 4));
ox += 4 * channels;
//logger.log(Level.TRACE, String.format("%1x", c));
//logger.log(Level.TRACE, "%1x".formatted(c));
}

// Update the step for the next sample
Expand All @@ -301,7 +301,7 @@ private static int encode(int channel,
//if (outBuffer != null)
// logger.log(Level.DEBUG, "");
d2 /= length; // be sure it's non-negative
//logger.log(Level.TRACE, String.format("ch%d: st %d->%d, d %.1f", channel, steps[sp], step, Math.sqrt(d2)));
//logger.log(Level.TRACE, "ch%d: st %d->%d, d %.1f".formatted(channel, steps[sp], step, Math.sqrt(d2)));
steps[sp] = step;

return (int) Math.sqrt(d2);
Expand Down Expand Up @@ -354,7 +354,7 @@ private static void encodeChannel(int channel,

s1[0] = s0;
encode(channel, channels, v, _iCoef[k], inBuffer, n0, s1, 0, null);
//logger.log(Level.TRACE, String.format(" s32 %d", s1[0]));
//logger.log(Level.TRACE, " s32 %d".formatted(s1[0]));

ss[0] = (3 * s0 + s1[0]) / 4;
s1[0] = ss[0];
Expand All @@ -372,7 +372,7 @@ private static void encodeChannel(int channel,
}
}
steps[sp] = smin;
//logger.log(Level.TRACE, String.format("kmin %d, smin %5d, ", kmin, smin));
//logger.log(Level.TRACE, "kmin %d, smin %5d, ".formatted(kmin, smin));
encode(channel, channels, v, _iCoef[kmin], inBuffer, length, steps, sp, outBuffer);
outBuffer[channel] = (byte) kmin;
}
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/vavi/sound/adpcm/vox/Vox.java
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public int encode(int samp) {
}

state.last = decode(code);
//logger.log(Level.TRACE, String.format("%04X -> %02X", samp, code));
//logger.log(Level.TRACE, "%04X -> %02X".formatted(samp, code));
return code;
}

Expand Down
2 changes: 1 addition & 1 deletion src/main/java/vavi/sound/adpcm/yamaha/Yamaha.java
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ public int encode(int samp) {
}

stat.last = decode(code);
//logger.log(Level.TRACE, String.format("%04X -> %02X", samp, code));
//logger.log(Level.TRACE, "%04X -> %02X".formatted(samp, code));
return code;
}
}
14 changes: 7 additions & 7 deletions src/main/java/vavi/sound/adpcm/ym2608/Ym2608.java
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,12 @@ public int encode(int pcm) {

// encoding process 2
long dn = pcm - state.xn;
//logger.log(Level.TRACE, String.format("%05d: %d, %d, %d", ccc, dn, pcm, state.xn)); // OK
//logger.log(Level.TRACE, "%05d: %d, %d, %d".formatted(ccc, dn, pcm, state.xn)); // OK
// encoding process 3, 4
// calc An from "I = | dn | / Sn"
// calc using integer part of production.
long i = (int) (((Math.abs(dn)) << 16) / ((state.stepSize) << 14));
//logger.log(Level.TRACE, String.format("%05d: %d", ccc, i)); // OK
//logger.log(Level.TRACE, "%05d: %d".formatted(ccc, i)); // OK
if (i > 7) {
i = 7;
}
Expand All @@ -62,7 +62,7 @@ public int encode(int pcm) {
// encoding process 5
// L3 + L2 / 2 + L1 / 4 + 1 / 8 * stepSize multiply 8 times and calc as integer
i = (adpcm * 2L + 1) * state.stepSize / 8;
//logger.log(Level.TRACE, String.format("%05d: %d, %d, %d", ccc, i, adpcm, state.stepSize)); // OK
//logger.log(Level.TRACE, "%05d: %d, %d, %d".formatted(ccc, i, adpcm, state.stepSize)); // OK

// if "1 - 2 * L4 -> L4" is 1 equals multiply -1
if (dn < 0) {
Expand All @@ -74,12 +74,12 @@ public int encode(int pcm) {
} else {
state.xn += i;
}
//logger.log(Level.TRACE, String.format("%05d: %d, %d", ccc, state.xn, i));
//logger.log(Level.TRACE, "%05d: %d, %d".formatted(ccc, state.xn, i));

// encode process 6
// update step size
state.stepSize = (stepsizeTable[adpcm] * state.stepSize) / 64;
//logger.log(Level.TRACE, String.format("%05d: %d, %d, %d", ccc, i, adpcm, state.stepSize)); // OK
//logger.log(Level.TRACE, "%05d: %d, %d, %d".formatted(ccc, i, adpcm, state.stepSize)); // OK

// encode process 7
if (state.stepSize < 127) {
Expand Down Expand Up @@ -108,7 +108,7 @@ public int decode(int adpcm) {
} else {
state.xn += i;
}
//logger.log(Level.TRACE, String.format("%05d: %d, %d, %d", state.count, state.xn, state.stepSize, adpcm)); // OK
//logger.log(Level.TRACE, "%05d: %d, %d, %d".formatted(state.count, state.xn, state.stepSize, adpcm)); // OK

// decode process 4
if (state.xn > 32767) {
Expand All @@ -125,7 +125,7 @@ public int decode(int adpcm) {
} else if (state.stepSize > 24576) {
state.stepSize = 24576;
}
//logger.log(Level.TRACE, String.format("%05d: %d, %d, %d", state.count, state.xn, state.stepSize, adpcm)); // OK
//logger.log(Level.TRACE, "%05d: %d, %d, %d".formatted(state.count, state.xn, state.stepSize, adpcm)); // OK

// store PCM
int pcm = (int) state.xn;
Expand Down
6 changes: 3 additions & 3 deletions src/main/java/vavi/sound/mfi/MfiDevice.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,12 @@ protected Info(String name,
this.version = version;
}

/** */
@Override
public final boolean equals(Object obj) {
return super.equals(obj);
}

/** */
@Override
public final int hashCode() {
return super.hashCode();
}
Expand All @@ -68,7 +68,7 @@ public final String getVersion() {
return version;
}

/** */
@Override
public final String toString() {
return name;
}
Expand Down
10 changes: 5 additions & 5 deletions src/main/java/vavi/sound/mfi/NoteMessage.java
Original file line number Diff line number Diff line change
Expand Up @@ -200,10 +200,10 @@ public void setVelocity(int velocity) {
@Override
public String toString() {
return "Note:" +
" delta=" + String.format("%02x", data[0]) +
" voice=" + String.format("%02x", voice) +
" note=" + String.format("%02x", note) +
" gateTime=" + String.format("%02x", gateTime) +
" velocity=" + String.format("%02x", velocity);
" delta=%02x".formatted(data[0]) +
" voice=%02x".formatted(voice) +
" note=%02x".formatted(note) +
" gateTime=%02x".formatted(gateTime) +
" velocity=%02x".formatted(velocity);
}
}
18 changes: 16 additions & 2 deletions src/main/java/vavi/sound/mfi/Track.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,24 @@
package vavi.sound.mfi;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.stream.Stream;


/**
* MFi Track.
*
* TODO is this class necessary?
*
* @author <a href="mailto:[email protected]">Naohide Sano</a> (nsano)
* @version 0.00 020627 nsano initial version <br>
* 0.10 020627 nsano refine <br>
* 0.11 030915 nsano add insert <br>
*/
public class Track {
public class Track implements Iterable<MfiEvent> {

/** */
/** includes sub messages of the header, audio track messages */
protected final List<MfiEvent> events;

/** Creates new Track */
Expand Down Expand Up @@ -53,5 +57,15 @@ public int size() {
return events.size();
}

@Override
public Iterator<MfiEvent> iterator() {
return events.iterator();
}

/** */
public Stream<MfiEvent> stream() {
return events.stream();
}

// TODO public long ticks()
}
22 changes: 19 additions & 3 deletions src/main/java/vavi/sound/mfi/vavi/AudioDataMessage.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import vavi.util.StringUtil;

import static java.lang.System.getLogger;
import static vavi.sound.mfi.vavi.VaviMfiFileFormat.DumpContext.getDC;


/**
Expand Down Expand Up @@ -187,7 +188,7 @@ public void readFrom(InputStream is)
int headerLength = dis.readUnsignedShort();
this.format = dis.readUnsignedByte();
this.attribute = dis.readUnsignedByte();
logger.log(Level.DEBUG, String.format("adat header: %d: f: %02x, a: %02x", headerLength, format, attribute));
logger.log(Level.DEBUG, "adat header: %d: f: %02x, a: %02x".formatted(headerLength, format, attribute));

// sub chunks
int l = 0;
Expand Down Expand Up @@ -278,7 +279,22 @@ public void sequence() throws InvalidMfiDataException {
int samplingBits = adpm.getSamplingBits();
int channels = adpm.getChannels();

AudioEngine engine = Factory.getAudioEngine(format);
engine.setData(id, -1, samplingRate, samplingBits, channels, data, false);
try {
AudioEngine engine = Factory.getAudioEngine(format);
engine.setData(id, -1, samplingRate, samplingBits, channels, data, false);
} catch (IllegalArgumentException e) {
logger.log(Level.ERROR, "cannot retrieve audio engine for: " + e.getMessage());
}
}

@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(TYPE).append("\n");
try (var dc = getDC().open()) {
subChunks.values().forEach(sc -> sb.append(dc.format(sc.toString())));
}
sb.setLength(sb.length() - 1);
return sb.toString();
}
}
14 changes: 13 additions & 1 deletion src/main/java/vavi/sound/mfi/vavi/HeaderChunk.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,14 @@
import vavi.sound.mfi.vavi.header.VersMessage;

import static java.lang.System.getLogger;
import static vavi.sound.mfi.vavi.VaviMfiFileFormat.DumpContext.getDC;


/**
* HeaderChunk.
*
* TODO this is file chunk? includes audio/track chunks
*
* @author <a href="mailto:[email protected]">Naohide Sano</a> (nsano)
* @version 0.00 070118 nsano initial version <br>
*/
Expand All @@ -52,7 +55,7 @@ class HeaderChunk {
/** when {@link #majorType} is {@link #MAJOR_TYPE_MUSIC}, fixed */
public static final int MINOR_TYPE_MUSIC = 0x00;

/** MFi data length ({@link #TYPE} + {@link #mfiDataLength} are excluded) */
/** MFi data length ({@link #TYPE} + {@code mfiDataLength} are excluded) */
private int mfiDataLength;

/** {@link #HEADER_LENGTH} + {@link #getSubChunksLength()} */
Expand Down Expand Up @@ -272,4 +275,13 @@ public static HeaderChunk readFrom(InputStream is)

return headerChunk;
}

@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(HeaderChunk.TYPE).append("\n");
// TODO subChunks should be one liner?
subChunks.values().forEach(sc -> sb.append(getDC().format(sc.toString().replaceAll("\n", " ↵ "))));
return sb.toString();
}
}
Loading
Loading