-
Notifications
You must be signed in to change notification settings - Fork 2
21 August 2026
I have used Qwen-TTS to generate a voice narration for the cartridge.
I started by adding the text from each frame separately, but then noticed that each time I run the model, the voice changes just a little bit, so I had to add the entire text and process it at once to keep the narration more consistent. I will chop it up into individual file frames with audacity later.
Additionally, this is encoded in 24k Hz .WAV, so I will have to convert it to the correct sampling first, then to .a18 (I believe, if I remember initial tests correctly).
Another strategy we could use is to separate the character-specific lines and generate a separate voice for each of those. This would be quite good, I think.
If I were to make this into an application, I would make each line tagged so they will be added together into separate files. Then, the model will switch voices and generate several audio files. I would then have to calculate the timestamps between narrated sentences, and stitch them together appropriately. I think this is quite do-able. A worry that comes up immediately is the notion of incorporating qwen-tts into this application: I must make the application and check how large the package would be -- I am worried that using an AI model will add a lot of data bloat which may be problematic for a low-powered computer. Regardless, I suppose I could direct the making of this application and have it at least exist even if bloated, and then refine it as times permits.
I also notice that qwen-tts takes quite a while to generate a .WAV file even from a short story. I would definitely have to incorporate a time-estimate for audio compilation.
I’ve made the data payload by hand. It works for 2 seconds before crashing, and I can hear the actual custom audio clearly!
I believe the reason it crashes is that the custom audio payload is being procedurally loaded and encountering partial codec blocks. I bet the audio conversion does not take in account the 40 byte rule, thus creating partial blocks. I will check that each frame’s data is divisible by 40, and pad what is required. I’m hoping this is the problem, as this is an easy fix.
I am so close to making a complete custom story. We are expecting (1) our second kid in a couple weeks, and (2) the restart of the school semester, so I figure that I have about 1 more week until this project reaches a mandatory pause. I think I can do it tonight!
- Write the narrative
- Tag each line per character
- Compile separate
.txtdocuments per character - Run Qwen-TTS per
.txt - Compile timestamps between lines
- Cut and stitch audio data as intended
- Refragment into 12 frames
- Manually cut into 12 frames
- Use Audacity to load each file, export as 16000hz, signed 16-bit PCM ‘.WAV’
- Use a1800_codec to encode each
.WAVinto.a18
- Use hex editor editor to make new file
- Paste Segment 1 template
- Paste two blank slides without lights
- Copy hex of each
.a18and paste. - Tag or bookmark each custom payload in the hex editor
- Paste the blank light template immediately after each payload (retain accurate payload bookmarks)
- Using John-K’s
format.mdinsert light commands between START and STOP markers of template
- For each payload bookmark, write the initial address serially in the Segment 1 pointer table
- Redirect both Segment 2 pointers to Region 1
- Redirect all Region 13-24 pointers to Region 12
- For each Region, follow the pointers to ensure the demarcation of the first byte of each respective Region/redirected Region.
- Fill rest of data space with ‘FF’ to the address of ‘0xFFFFFF’ (1MiB)
- Plug in Arduino to PC
- Insert the LTSDM shield
- Ensure Arduino is flashed with the right program
- Insert a cartridge into the shield
- Use PC application to connect to Arduino
- Use PC application to get proper JEDEC
- Load custom binary file into application
- Flash binary to cartridge
==The cartridge is ready to play on the projector==
/Binary/custom/ratadon/ratadon.bin
| Region (#) | Initial Length of Audio (Bytes) | Excess (Bytes) | Repaired Length (Bytes) |
|---|---|---|---|
| 1 | 8566 | 6 | 8560 |
| 2 | 19406 | 6 | 19400 |
| 3 | 23486 | 6 | 23480 |
| 4 | 28726 | 6 | 28720 |
| 5 | 48846 | 6 | 48840 |
| 6 | 42166 | 6 | 42160 |
| 7 | 37686 | 6 | 37686 |
| 8 | 32806 | 6 | 32800 |
| 9 | 32806 | 6 | 32800 |
| 10 | 37286 | 6 | 37286 |
| 11 | 23846 | 6 | 23840 |
| 12 | 26086 | 6 | 26080 |
Where:
- Excess
$= {Initial}%40$ - Repaired Length
$= {Initial}-{Excess}$
**Remember to fill the deficit to 1MiB after all trims.
*Remember to reset Segment 1 after length adjustments.
It appears that the converted .a18 file is 6 bytes over what it usually should be always. Its the header. I cannot delete that...
The audio payloads themselves are all divisible by 40. This destroys my assumption that the conversion allowed partial .a18 blocks, which caused the crash.
Questions to answer:
- Why is the projector crashing after 2 seconds?
- Why does the projector actually play some of the custom audio at all, but still crash?
Evidence:
- Since the raw template is accepted (which means, it doesn't crash at all) by the projector, and this audio is not, we can conclude that the new audio payload is the problem.
Culprit Theories:
- Payload content
- Payload length
The culprit, "Payload length," would be disproven if my custom audio payloads are within what we assume the spec (audio body itself must be divisible in complete, 40-byte blocks of hex) is true. This disproof happened just now. So "Payload length" is false as long as our spec is true. This means that "Payload content" is true, or our spec is false.
Content
- Scan for any START or STOP codes from the light table.
- absence of START or STOP codes would just eliminate this theory, we would have to think of other reasons content is the culprit.
Spec
- Run the falsely-repaired result of Attempt 1
- if it runs, the spec is false.
- if it crashes too, the spec if fortified true.
Yes, but we also added 6 bytes to each audio for Regions 1-12 for the light table... maybe its the blank light table as opposed to the header?
The falsely-trimmed audio worked for 2 seconds, but then the lights flickered, and the audio distorted, and then the carousel sound went (but with no actual, physical carousel turn at all). Then it crashed. It crashing too would suggest that the shared attributes were errors due to content as opposed to length. Both running for 2 seconds = it's content. Both projecting custom audio = it's content. Audio distortion = this particular error is caused by length mismatch. Carousel sound (but no physical movement) = this particular error is caused by length mismatch.
If the first crashed test does not have the trim-unique version's audio distortions at the end, and carousel sound (but no physical movement), then we can conclude that these mentioned error attributes are both due to length only.
Results
- Just kidding... the Attempt 4 is the same as Attempt 1, which contained all of each other's error attributes (even the length-dependent ones). This means that the "length-dependent" attributes were universal like the content-dependent would, thus a separate error is preventing from audio proceeding beyond 2 seconds.
All length errors were content errors, so perhaps a partial 40-byte block could, in fact be ran successfully. Yet to be tested.
- Lets comb each Region Body over for START and STOP codes from a light table.
- We will scan for
04 F0,04 F0 00,04 F1, and04 F1 00to see if there are any START/STOP codes (I am unsure if the START and STOP need a third byte assigned as00, or if it goes straight into lights mode waiting for a value, resulting in a crash) *Let us comb throughratadon.bin(we can assume the results ofratadon-trimmed-6.binwill be identical toratadon.bin) sinceratadon-trimmed-6.bin=ratadon.binbut with less info (not differing info that is kept).
- We will scan for
ratadon.bin Instances of following Hex Patterns
| Region | 04 F0 |
04 F0 00 |
04 F1 |
04 F1 00 |
|---|---|---|---|---|
| All | 19 | 13 | 19 | 12 |
While each three-hex pattern MUST only instantiate = n slides (ie: 12 times).
Since there was an extra on the 04 F0 00 code (the full START command). I predict that feeding this in no matter where it occurs (after a body, during a body) that the light-table index will appear and the remainder of the data reads colour commands (but there should be a much smaller chance of colour commands being set forth. These commands only start with 04, 08, and 0C. This would mean that the colour command reader would read any 1 byte and activate. What if the code also looks for "is this command the right length to be that length of light command (RGB channels)?"
First, let us isolate where that extra 04 F0 00 instant landed.
Address = 0x2E9F9-0x2E9FB.
Region Location = Region 8. This would be slide 8.
We should assume that the program reads until this part of the code (preparing it for consumption) which would be that 2 seconds, then crashes. If we change 04 F0 00 to 05 F0 00 and see what happens. If it plays, then it was the early call of a light table. If it does not play, then we can assume that the light table is instantiated AT THE END of each audio payload data dictated by each header, and an early declaration of a light table would be ineffectual.
Second, we can also take note of all the starters that occur in the potential-light-zone (anything after when this early light table opening is declared -- in the middle of Region 8. Region 8+ range...
Starter Bytes Count since 0x3307E
*04
|
08 |
`0C |
|---|---|---|
| 791 | 807 | 614 |
*there should be a minimum of 2/Region beyond the matching address since there is a START and a STOP code included in each Region highlighted.
Search Range: 0x3307E-0x5857B
Since there are 04, 08, and 0C within the post-match payload data, we know that the first byte alone will not trigger a light table.
I can also see that the correction of this mutation point will not run into other potential mutation points, thus strengthening its answer.
04 F0 00 match address = 0x2E9F9-2E9fB = Region 7.
Starter bytes since 0x2E9F9
04 |
08 |
0C |
|---|---|---|
| 904 | 920 | 676 |
There are a lot of light command starts = must narrow down. When do these checks break, if at all?
Two-Starter bytes since 0x2E9F9
|04 80|04 81|04 82|08 80|08 81|08 82|0C 80|0C 81|0C 82|
|3|5|3|5|5|4|4|2|4|
If any of these codes start a light command, their instance should be 0 since the only light commands in this entire file are START/STOP (04 F0 and 04 F1), so no channel declarations would be accepted = all of these should lie within audio body = they should not be here = if they DO appear, then this means that this two-byte sequence is not checked, and is meaningless. What about detecting if the starter byte is followed by enough channels and data to give evidence to the notion that they are firing at least the first command as a random value until the first mismatched channel byte (anything other than F0 F1 80 81 82) causing the crash.
For this test, we need to check each permutation of n-byte hex sequences to be 08 or 0C calibre commands. The 04 8x data is already collected. Any of these instances could be accepted since the next byte value just simply has to range from the entire spectrum of numbers (unconstrained). The existence of 04 8x is inconclusive if the light commands are prematurely triggering. We will search for the second channel value in these potential light commands.
We need to track if the reported instances follow the light command pattern:
- 08-0 = 5 instances
- 08-1 = 5 instances
- 08-2 = 4 instances
- 0C-0 = 4 instances
- 0C-1 = 2 instances
- 0C-2 = 4 instances
Does this point-mutation have the structure of a proper light command?
(byte #4 = either 80, 81, or 82; this second channel byte is never the same as the first, because it would be ineffectual. ("0801" = 08 byte 1, 80 byte 2, point 1 mutation; check if two bytes in from there are 80, 81, or 82 as the value of n+2; for 0C check if two and four bytes in from there are only one of the following each 80 81 82).
- 0801 = x
- 0802 = yes
- 0803 = x
- 0804 = x
- 0805 = x
- 0811 = x
- 0812 = x
- 0813 = x
- 0814 = x
- 0815 = x
- 0821 = x
- 0822 = x
- 0823 = x
- 0824 = x
- 0C01 = x
- 0C02 = x
- 0C03 = x
- 0C04 = x
- 0C11 = x
- 0C12 = x
- 0C21 = x
- 0C22 = x
- 0C23 = x
- 0C24 = x
0802 address = 0x39A07-39A0B (the only complete command given in audio body)
- If we edit this first byte to make it look not like a light command, then it still crashes, the crash is not due to a complete light command instantiating where it should not.
- DOUBLE CHECK: This is not a light command... (recheck the address of 0802 in
ratadon.bin)
If this is searched on a known-working binary file, and we still see 08 80 with another 80, 81, 82 on the fourth byte, then we can conclude that a "complete" light command injected halfway through the audio body does not trigger any light commands UNLESS the light command START was played.
Searching for 08 80 on a known file: 221 instances
Point Mutation Addresses outside a designated light table
- 939F-93A3
- 10B9F-10BA0
- 1874D-1874E
- 1EBC6-1EBC7
- 289E9-289EA
- 2A15C-2A15D
- 30E9B-30E9C
None of these are full light commands. Maybe changing the full mismatched light command to see if it was this insertion that derailed the whole file. Turn first 08 byte to a 07 so it preserves as much mathematical difference, but also falls outside the eligible light commands.
Result
- Crashes exactly the same. This strongly suggests that all aspects of the crash are due to "bad content" of the
.a18bodies - Light commands likely do not work when declared before a light table START is declared.
What byte sequences in the custom .a18 payload makes the projector crash? Because it is not any custom data in these regions: we can see that the MVP test ran without error, so modding the data itself is not what makes the projector crash. Adherence to 40-byte codec blocks also has nothing to do with the crash, and declaring premature light commands before a START also does not make the projector crash. It must be the content somehow...
What happens if we dreamsmith extract ratadon.bin? It should still chop up correctly.
- It doesn't. Error finding Hz at 0x6dd6, 0x 1419 instead
- Perhaps the a1800_codec made an error on how big the payload is? The header should declare how big the body is. If there is a mismatch here, then the a1800_codec is broken.
- Mismatch detected in Region 1: audio body = 8554; header = 8560
- Mismatch detected in Region 12: audio body =26074; header = 26080
- From these mismatches, we can conclude that this is due to the 6-byte trim I gave it, meaning the a1800_codec works properly.
- Perhaps the reason it is not extracting is because this
ratadon.binis a trimmed version, thus will not cut correctly.
- Since I've realized I deleted the original
ratadon.binpayload, I've made a new one manually. I feel like some of the pointers are different than before. It seems to extract throughdreamsmithwithout errors this time. - I will compile using
dreamsmith. - The errors are even worse now, the carousel actually turns once when the sound plays. All other errors persist prior to the crash.
- It looks like I might have flashed
ratadon.bininstead ofratadon2.bin -
radataon2.binre-flashed. Still the same errors (with the carousel actually moving).
| ⟵ Older | Table of Contents | Newer ⟶ |
|---|